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

row basics

This commit is contained in:
Ruben Perez
2022-08-08 19:01:23 +02:00
parent 35e4b735fc
commit 68fcceccb3
5 changed files with 171 additions and 503 deletions

View File

@@ -1,230 +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_IMPL_RESULTSET_HPP
#define BOOST_MYSQL_IMPL_RESULTSET_HPP
#pragma once
#include <boost/mysql/resultset.hpp>
#include <boost/mysql/detail/network_algorithms/read_row.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/asio/bind_executor.hpp>
#include <boost/asio/post.hpp>
#include <cassert>
#include <limits>
#include <memory>
template <class Stream>
std::vector<boost::mysql::row> boost::mysql::resultset<Stream>::read_many(
std::size_t count,
error_code& err,
error_info& info
)
{
assert(valid());
detail::clear_errors(err, info);
std::vector<row> res;
for (std::size_t i = 0; i < count; ++i)
{
row r;
if (this->read_one(r, err, info))
{
res.push_back(std::move(r));
}
else
{
break;
}
}
return res;
}
template <class Stream>
std::vector<boost::mysql::row> boost::mysql::resultset<Stream>::read_many(
std::size_t count
)
{
detail::error_block blk;
auto res = read_many(count, blk.err, blk.info);
blk.check();
return res;
}
template <class Stream>
std::vector<boost::mysql::row> boost::mysql::resultset<Stream>::read_all(
error_code& err,
error_info& info
)
{
return read_many(std::numeric_limits<std::size_t>::max(), err, info);
}
template <class Stream>
std::vector<boost::mysql::row> boost::mysql::resultset<Stream>::read_all()
{
return read_many(std::numeric_limits<std::size_t>::max());
}
template<class Stream>
struct boost::mysql::resultset<Stream>::read_many_op
: boost::asio::coroutine
{
struct impl_struct
{
resultset<Stream>& parent_resultset;
std::vector<row> rows;
row current_row;
std::size_t remaining;
error_info& output_info;
bool cont {false};
impl_struct(resultset<Stream>& obj, error_info& output_info, std::size_t count):
parent_resultset(obj),
remaining(count),
output_info(output_info)
{
};
void row_received()
{
rows.push_back(std::move(current_row));
current_row = row();
--remaining;
}
};
std::shared_ptr<impl_struct> impl_;
read_many_op(
resultset<Stream>& obj,
error_info& output_info,
std::size_t count
) :
impl_(std::make_shared<impl_struct>(obj, output_info, count))
{
};
template <class Self>
auto bind_handler(Self& self, error_code err) ->
boost::asio::executor_binder<
decltype(std::bind(std::move(self), err, detail::read_row_result::eof)),
decltype(boost::asio::get_associated_executor(self, impl_->parent_resultset.get_executor()))
>
{
auto executor = boost::asio::get_associated_executor(
self,
impl_->parent_resultset.get_executor()
);
return boost::asio::bind_executor(
executor,
std::bind(std::move(self), err, detail::read_row_result::eof)
);
}
template<class Self>
void operator()(
Self& self,
error_code err = {},
detail::read_row_result result = detail::read_row_result::error
)
{
impl_struct& impl = *impl_;
BOOST_ASIO_CORO_REENTER(*this)
{
while (!impl.parent_resultset.complete() && impl.remaining > 0)
{
impl.cont = true;
BOOST_ASIO_CORO_YIELD detail::async_read_row(
impl.parent_resultset.deserializer_,
*impl.parent_resultset.channel_,
impl.parent_resultset.meta_.fields(),
impl.current_row,
impl.parent_resultset.ok_packet_buffer_,
impl.parent_resultset.ok_packet_,
std::move(self),
impl.output_info
);
if (result == detail::read_row_result::error)
{
self.complete(err, std::move(impl.rows));
BOOST_ASIO_CORO_YIELD break;
}
else if (result == detail::read_row_result::eof)
{
impl.parent_resultset.eof_received_ = true;
}
else
{
impl.row_received();
}
}
if (!impl.cont)
{
// Ensure we call handler as if dispatched using post
// through the correct executor
BOOST_ASIO_CORO_YIELD
boost::asio::post(bind_handler(self, err));
}
self.complete(err, std::move(impl.rows));
}
}
};
template <class Stream>
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(
void(::boost::mysql::error_code, std::vector<::boost::mysql::row>)) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
void(boost::mysql::error_code, std::vector<boost::mysql::row>)
)
boost::mysql::resultset<Stream>::async_read_many(
std::size_t count,
error_info& output_info,
CompletionToken&& token
)
{
assert(valid());
output_info.clear();
return boost::asio::async_compose<
CompletionToken,
void(error_code, std::vector<row>)
>(
read_many_op(*this, output_info, count),
token,
*this
);
}
template <class Stream>
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(
void(::boost::mysql::error_code, std::vector<::boost::mysql::row>)) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
void(boost::mysql::error_code, std::vector<boost::mysql::row>)
)
boost::mysql::resultset<Stream>::async_read_all(
error_info& info,
CompletionToken&& token
)
{
return async_read_many(
std::numeric_limits<std::size_t>::max(),
info,
std::forward<CompletionToken>(token)
);
}
#endif

View File

@@ -0,0 +1,144 @@
//
// 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_IMPL_ROW_HPP
#define BOOST_MYSQL_IMPL_ROW_HPP
#pragma once
#include <boost/mysql/row.hpp>
inline void boost::mysql::row::rebase_strings(
const char* old_buffer_base
) noexcept
{
auto diff = string_buffer_.data() - old_buffer_base;
for (auto& f: fields_)
{
const boost::string_view* str = f.if_string();
if (str)
{
f = field_view(boost::string_view(
str->data() + diff,
str->size()
));
}
}
}
inline void boost::mysql::row::copy_strings()
{
// Calculate size
std::size_t size = 0;
for (const auto& f: fields_)
{
const boost::string_view* str = f.if_string();
if (str)
{
size += str->size();
}
}
// Make space
string_buffer_.resize(size);
// Copy the strings
std::size_t offset = 0;
for (auto& f: fields_)
{
const boost::string_view* str = f.if_string();
if (str)
{
std::memcpy(string_buffer_.data() + offset, str->data(), str->size());
f = field_view(boost::string_view(string_buffer_.data() + offset, str->size()));
offset += str->size();
}
}
}
inline boost::mysql::row::iterator boost::mysql::row::to_row_it(
std::vector<field_view>::iterator it
) noexcept
{
return it == fields_.end() ? nullptr : fields_.data() + (it - fields_.begin());
}
inline std::vector<boost::mysql::field_view>::iterator boost::mysql::row::to_vector_it(
iterator it
) noexcept
{
return it == nullptr ? fields_.end() : fields_.begin() + (it - fields_.data());
}
boost::mysql::row::row(
const row& other
) :
fields_(other.fields_),
string_buffer_(other.string_buffer_)
{
rebase_strings(other.string_buffer_.data());
}
const boost::mysql::row& boost::mysql::row::operator=(
const row& rhs
)
{
fields_ = rhs.fields_;
string_buffer_ = rhs.string_buffer_;
rebase_strings(rhs.string_buffer_.data());
return *this;
}
inline boost::string_view boost::mysql::row::copy_string(
boost::string_view v
)
{
const char* old_buffer_base = string_buffer_.data();
std::size_t old_buffer_size = string_buffer_.size();
string_buffer_.insert(string_buffer_.end(), v.data(), v.data() + v.size());
if (string_buffer_.data() != old_buffer_base)
rebase_strings(old_buffer_base);
return boost::string_view(string_buffer_.data() + old_buffer_size, v.size());
}
inline boost::mysql::row::iterator boost::mysql::row::insert(
iterator before,
field_view v
)
{
const auto* str = v.if_string();
if (str)
{
v = field_view(copy_string(*str));
}
auto res = fields_.insert(to_vector_it(before), v);
return to_row_it(res);
}
// inline boost::mysql::row::iterator boost::mysql::row::insert(
// iterator before,
// std::initializer_list<field_view> v
// )
// {
// // Calculate the extra size required for the strings
// std::size_t new_string_size = 0;
// for (const auto& f: v)
// {
// const auto* str = f.if_string();
// if (str)
// {
// new_string_size += str->size();
// }
// }
// }
#endif

View File

@@ -8,18 +8,12 @@
#ifndef BOOST_MYSQL_ROW_HPP
#define BOOST_MYSQL_ROW_HPP
#include <boost/mysql/detail/auxiliar/bytestring.hpp>
#include <boost/mysql/detail/auxiliar/container_equals.hpp>
#include <boost/mysql/field_view.hpp>
#include <algorithm>
#include <boost/mysql/row_view.hpp>
#include <boost/utility/string_view_fwd.hpp>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <initializer_list>
#include <iterator>
#include <vector>
namespace boost {
namespace mysql {
@@ -48,13 +42,11 @@ namespace mysql {
*/
class row
{
std::vector<field_view> fields_;
std::vector<char> string_buffer_;
public:
row() = default;
row(const row&) = delete; // TODO
inline row(const row&);
row(row&&) = default;
row& operator=(const row&) = delete; // TODO
inline const row& operator=(const row&);
row& operator=(row&&) = default;
~row() = default;
@@ -71,21 +63,26 @@ public:
bool empty() const noexcept { return fields_.empty(); }
std::size_t size() const noexcept { return fields_.size(); }
iterator insert(iterator before, field_view v);
iterator insert(iterator before, std::initializer_list<field_view> v);
inline iterator insert(iterator before, field_view v);
inline iterator insert(iterator before, std::initializer_list<field_view> v);
template <class FwdIt>
iterator insert(iterator before, FwdIt first, FwdIt last);
iterator replace(iterator pos, field_view v);
iterator replace(iterator first, iterator last, std::initializer_list<field_view> v);
inline iterator replace(iterator pos, field_view v);
inline iterator replace(iterator first, iterator last, std::initializer_list<field_view> v);
template <class FwdIt>
iterator replace(iterator first, iterator last, FwdIt other_first, FwdIt other_last);
inline iterator replace(iterator first, iterator last, FwdIt other_first, FwdIt other_last);
iterator erase(iterator pos);
iterator erase(iterator first, iterator last);
inline iterator erase(iterator pos);
inline iterator erase(iterator first, iterator last);
void push_back(field_view v);
void pop_back();
inline void push_back(field_view v);
inline void pop_back();
operator row_view() const noexcept
{
return row_view(fields_.data(), fields_.size());
}
@@ -105,247 +102,21 @@ public:
// TODO: hide these
const std::vector<field_view>& fields() const noexcept { return fields_; }
std::vector<field_view>& values() noexcept { return fields_; }
void copy_strings()
{
// Calculate size
std::size_t size = 0;
for (const auto& f: fields_)
{
const boost::string_view* str = f.if_string();
if (str)
{
size += str->size();
}
}
// Make space
string_buffer_.resize(size);
// Copy the strings
std::size_t offset = 0;
for (auto& f: fields_)
{
const boost::string_view* str = f.if_string();
if (str)
{
std::memcpy(string_buffer_.data() + offset, str->data(), str->size());
f = field_view(boost::string_view(string_buffer_.data() + offset, str->size()));
offset += str->size();
}
}
}
};
class row_view
{
const field_view* fields_ {};
std::size_t size_ {};
public:
row_view() = default;
row_view(const field_view* f, std::size_t size) noexcept : fields_ {f}, size_{size} {}
row_view(const row& r) noexcept :
fields_(r.begin()),
size_(r.size())
{
}
using iterator = const field_view*;
using const_iterator = iterator;
iterator begin() const noexcept { return fields_; }
iterator end() const noexcept { return fields_ + size_; }
field_view at(std::size_t i) const;
field_view operator[](std::size_t i) const noexcept { return fields_[i]; }
field_view front() const noexcept { return fields_[0]; }
field_view back() const noexcept { return fields_[size_ - 1]; }
bool empty() const noexcept { return size_ == 0; }
std::size_t size() const noexcept { return size_; }
};
class rows
{
inline void copy_strings();
private:
std::vector<field_view> fields_;
std::vector<char> string_buffer_;
std::size_t num_columns_ {};
void rebase_strings(const char* old_buffer_base)
{
const char* new_buffer_base = string_buffer_.data();
auto diff = new_buffer_base - old_buffer_base;
if (diff)
{
for (auto& f: fields_)
{
const boost::string_view* str = f.if_string();
if (str)
{
f = field_view(boost::string_view(
str->data() + diff,
str->size()
));
}
}
}
}
public:
rows() = default;
rows(std::size_t num_columns) noexcept : num_columns_(num_columns) {}
rows(const rows&) = delete; // TODO
rows(rows&&) = default;
const rows& operator=(const rows&) = delete; // TODO
rows& operator=(rows&&) = default;
class iterator;
using const_iterator = iterator;
// TODO: add other standard container members
iterator begin() const noexcept { return iterator(this, 0); }
iterator end() const noexcept { return iterator(this, size()); }
row_view at(std::size_t i) const { /* TODO: check idx */ return (*this)[i]; }
row_view operator[](std::size_t i) const noexcept
{
std::size_t offset = num_columns_ * i;
return row_view(fields_.data() + offset, num_columns_);
}
row_view front() const noexcept { return (*this)[0]; }
row_view back() const noexcept { return (*this)[size() - 1]; }
bool empty() const noexcept { return fields_.empty(); }
std::size_t size() const noexcept { return fields_.size() / num_columns_; }
// TODO: hide these
std::vector<field_view>& fields() noexcept { return fields_; }
void copy_strings(std::size_t field_offset)
{
// Calculate the extra size required for the new strings
std::size_t size = 0;
for (const auto* f = fields_.data() + field_offset; f != fields_.data() + fields_.size(); ++f)
{
const boost::string_view* str = f->if_string();
if (str)
{
size += str->size();
}
}
// Make space and rebase the old strings
const char* old_buffer_base = string_buffer_.data();
std::size_t old_buffer_size = string_buffer_.size();
string_buffer_.resize(size);
rebase_strings(old_buffer_base);
// Copy the strings
std::size_t offset = old_buffer_size;
for (auto& f: fields_)
{
const boost::string_view* str = f.if_string();
if (str)
{
std::memcpy(string_buffer_.data() + offset, str->data(), str->size());
f = field_view(boost::string_view(string_buffer_.data() + offset, str->size()));
offset += str->size();
}
}
}
void clear() noexcept
{
fields_.clear();
string_buffer_.clear();
}
class iterator
{
const rows* obj_;
std::size_t row_num_;
public:
using value_type = row_view; // TODO: should this be row?
using reference = row_view;
using pointer = void const*;
using difference_type = std::ptrdiff_t;
using iterator_category = std::bidirectional_iterator_tag;
// TODO: this can be made random access
iterator(const rows* obj, std::size_t rownum) noexcept : obj_(obj), row_num_(rownum) {}
iterator& operator++() noexcept { ++row_num_; return *this; }
iterator operator++(int) noexcept { return iterator(obj_, row_num_ + 1); }
iterator& operator--() noexcept { --row_num_; return *this; }
iterator operator--(int) noexcept { return iterator(obj_, row_num_ - 1); }
reference operator*() const noexcept { return (*obj_)[row_num_]; }
bool operator==(const iterator& rhs) const noexcept { return obj_ == rhs.obj_&& row_num_ == rhs.row_num_; }
bool operator!=(const iterator& rhs) const noexcept { return !(*this == rhs); }
};
inline iterator to_row_it(std::vector<field_view>::iterator it) noexcept;
inline std::vector<field_view>::iterator to_vector_it(iterator it) noexcept;
inline boost::string_view copy_string(boost::string_view v);
inline void rebase_strings(const char* old_buffer_base) noexcept;
};
class rows_view
{
const field_view* fields_ {};
std::size_t num_values_ {};
std::size_t num_columns_ {};
public:
rows_view() = default;
rows_view(const field_view* fields, std::size_t num_values, std::size_t num_columns) noexcept :
fields_(fields),
num_values_(num_values),
num_columns_(num_columns)
{
}
class iterator;
using const_iterator = iterator;
// TODO: add other standard container members
iterator begin() const noexcept { return iterator(this, 0); }
iterator end() const noexcept { return iterator(this, size()); }
row_view at(std::size_t i) const { /* TODO: check idx */ return (*this)[i]; }
row_view operator[](std::size_t i) const noexcept
{
std::size_t offset = num_columns_ * i;
return row_view(fields_ + offset, num_columns_);
}
row_view front() const noexcept { return (*this)[0]; }
row_view back() const noexcept { return (*this)[size() - 1]; }
bool empty() const noexcept { return num_values_ == 0; }
std::size_t size() const noexcept { return num_values_ / num_columns_; }
class iterator
{
const rows_view* obj_;
std::size_t row_num_;
public:
using value_type = row_view; // TODO: should this be row?
using reference = row_view;
using pointer = void const*;
using difference_type = std::ptrdiff_t;
using iterator_category = std::bidirectional_iterator_tag;
// TODO: this can be made random access
iterator(const rows_view* obj, std::size_t rownum) noexcept : obj_(obj), row_num_(rownum) {}
iterator& operator++() noexcept { ++row_num_; return *this; }
iterator operator++(int) noexcept { return iterator(obj_, row_num_ + 1); }
iterator& operator--() noexcept { --row_num_; return *this; }
iterator operator--(int) noexcept { return iterator(obj_, row_num_ - 1); }
reference operator*() const noexcept { return (*obj_)[row_num_]; }
bool operator==(const iterator& rhs) const noexcept { return obj_ == rhs.obj_&& row_num_ == rhs.row_num_; }
bool operator!=(const iterator& rhs) const noexcept { return !(*this == rhs); }
};
};
/**
* \relates row
* \brief Compares two rows.
*/
inline bool operator==(const row& lhs, const row& rhs)
{
return detail::container_equals(lhs.fields(), rhs.fields());
return row_view(lhs) == row_view(rhs);
}
/**
@@ -354,28 +125,11 @@ inline bool operator==(const row& lhs, const row& rhs)
*/
inline bool operator!=(const row& lhs, const row& rhs) { return !(lhs == rhs); }
/**
* \relates row
* \brief Streams a row.
*/
inline std::ostream& operator<<(std::ostream& os, const row& value)
{
os << '{';
const auto& arr = value.fields();
if (!arr.empty())
{
os << arr[0];
for (auto it = std::next(arr.begin()); it != arr.end(); ++it)
{
os << ", " << *it;
}
}
return os << '}';
}
} // mysql
} // boost
#include <boost/mysql/impl/row.ipp>
#endif

View File

@@ -58,6 +58,6 @@ inline std::ostream& operator<<(std::ostream& os, const row_view& value);
} // mysql
} // boost
#include <boost/mysql/impl/row_view.hpp>
#include <boost/mysql/impl/row_view.ipp>
#endif