mirror of
https://github.com/boostorg/mysql.git
synced 2026-02-14 12:52:17 +00:00
row basics
This commit is contained in:
@@ -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
|
||||
144
include/boost/mysql/impl/row.ipp
Normal file
144
include/boost/mysql/impl/row.ipp
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user