8#ifndef BOOST_SQLITE_STATIC_RESULTSET_HPP
9#define BOOST_SQLITE_STATIC_RESULTSET_HPP
11#include <boost/sqlite/detail/config.hpp>
12#include <boost/sqlite/resultset.hpp>
13#include <boost/sqlite/connection.hpp>
15#include <boost/describe/members.hpp>
19#if __cplusplus >= 202002L
20#include <boost/pfr/core.hpp>
21#include <boost/pfr/core_name.hpp>
22#include <boost/pfr/traits.hpp>
25BOOST_SQLITE_BEGIN_NAMESPACE
31struct value_to_tag {};
33inline value tag_invoke(value_to_tag<value>,
const field & f) {
return f.get_value();}
34inline int tag_invoke(value_to_tag<int>,
const field & f) {
return f.get_int();}
35inline sqlite_int64 tag_invoke(value_to_tag<sqlite_int64>,
const field & f) {
return f.get_int64();}
36inline double tag_invoke(value_to_tag<double>,
const field & f) {
return f.get_double();}
38template<
typename Allocator,
typename Traits>
39inline std::basic_string<char, Allocator, Traits>
40 tag_invoke(value_to_tag<std::basic_string<char, Allocator, Traits>>,
const field & f)
45inline string_view tag_invoke(value_to_tag<string_view>,
const field & f) {
return f.get_text();}
46inline blob tag_invoke(value_to_tag<blob>,
const field & f) {
return blob(f.get_blob());}
47inline blob_view tag_invoke(value_to_tag<blob_view>,
const field & f) {
return f.get_blob();}
51struct check_columns_tag {};
55struct convert_row_tag {};
57template<
typename ... Args>
58void tag_invoke(check_columns_tag<std::tuple<Args...>>,
const resultset & r,
59 system::error_code &ec, error_info & ei)
61 if (r.column_count() !=
sizeof...(Args))
63 BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
64 ei.format(
"Tuple size doesn't match column count [%d != %d]",
sizeof...(Args), r.column_count());
68template<
typename Tuple, std::size_t ... Ns>
69Tuple convert_row_to_tuple_impl(convert_row_tag<Tuple>,
const row & r,
70 mp11::index_sequence<Ns...>)
72 return Tuple{ tag_invoke(value_to_tag<
typename std::tuple_element<Ns, Tuple>::type>{}, r[Ns])... };
77template<
typename ... Args>
78std::tuple<Args...> tag_invoke(convert_row_tag<std::tuple<Args...>> tag,
const row & r)
80 return convert_row_to_tuple_impl(tag, r, mp11::make_index_sequence<
sizeof...(Args)>{});
83#if defined(BOOST_DESCRIBE_CXX14)
85template<typename T, typename = typename std::enable_if<describe::has_describe_members<T>::value>::type>
86void tag_invoke(check_columns_tag<T>,
const resultset & r,
87 system::error_code &ec, error_info & ei)
89 using mems = boost::describe::describe_members<T, describe::mod_public>;
90 constexpr std::size_t sz = mp11::mp_size<mems>();
91 if (r.column_count() != sz)
93 BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
94 ei.format(
"Describe size doesn't match column count [%d != %d]", sz, r.column_count());
98 std::array<bool, sz> found;
99 std::fill(found.begin(), found.end(),
false);
101 for (std::size_t i = 0ul; i < r.column_count(); i++)
104 boost::mp11::mp_for_each<mp11::mp_iota_c<sz>>(
107 auto d = mp11::mp_at_c<mems, sz>();
108 if (d.name == r.column_name(i))
117 BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
118 ei.format(
"Column %Q not found in described struct.", r.column_name(i));
127 auto itr = std::find(found.begin(), found.end(),
false);
128 if (itr != found.end())
130 mp11::mp_with_index<sz>(
131 std::distance(found.begin(), itr),
134 auto d = mp11::mp_at_c<mems, sz>();
135 BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
136 ei.format(
"Described field %Q not found in resultset struct.", d.name);
141template<typename T, typename = typename std::enable_if<describe::has_describe_members<T>::value>::type>
142T tag_invoke(convert_row_tag<T> tag,
const row & r)
147 boost::mp11::mp_for_each<boost::describe::describe_members<T, describe::mod_public> >(
150 if (D.name == c.column_name())
152 auto & r = res.*D.pointer;
153 r = tag_invoke(value_to_tag<
typename std::decay<
decltype(r)>::type>{}, c);
162#if __cplusplus >= 202002L
165 requires (std::is_aggregate_v<T> && !describe::has_describe_members<T>::value)
166void tag_invoke(check_columns_tag<T>,
const resultset & r,
167 system::error_code &ec, error_info & ei)
169 constexpr std::size_t sz = pfr::tuple_size_v<T>;
170 if (r.column_count() != sz)
172 BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
173 ei.format(
"Describe size doesn't match column count [%d != %d]", sz, r.column_count());
177 std::array<bool, sz> found;
178 std::fill(found.begin(), found.end(),
false);
180 for (std::size_t i = 0ul; i < r.column_count(); i++)
183 boost::mp11::mp_for_each<mp11::mp_iota_c<sz>>(
186 if (pfr::get_name<sz, T>() == r.column_name(i))
195 BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
196 ei.format(
"Column %Q not found in struct.", r.column_name(i));
205 auto itr = std::find(found.begin(), found.end(),
false);
206 if (itr != found.end())
208 mp11::mp_with_index<sz>(
209 std::distance(found.begin(), itr),
212 BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
213 ei.format(
"PFR field %Q not found in resultset struct.", pfr::get_name<sz, T>() );
219 requires (std::is_aggregate_v<T> && !describe::has_describe_members<T>::value)
220T tag_invoke(convert_row_tag<T> tag,
const row & r)
225 boost::mp11::mp_for_each<mp11::mp_iota_c<pfr::tuple_size_v<T>>>(
229 if (pfr::get_name<D, T>() == c.column_name().c_str())
231 auto & r = pfr::get<D()>(res);
232 r = tag_invoke(value_to_tag<std::decay_t<
decltype(r)>>{}, c);
277 return detail::tag_invoke(detail::convert_row_tag<T>{}, result_.current());
280 bool done()
const {
return result_.done();}
284 BOOST_SQLITE_DECL
bool read_next(system::error_code & ec,
error_info & ei) {
return result_.read_next(); }
285 BOOST_SQLITE_DECL
bool read_next() {
return result_.read_next(); }
289 std::size_t column_count()
const {
return result_.column_count(); }
291 core::string_view
column_name(std::size_t idx)
const {
return result_.column_name(idx); }
294 core::string_view
table_name(std::size_t idx)
const {
return result_.table_name(idx);}
296 core::string_view
column_origin_name(std::size_t idx)
const {
return result_.column_origin_name(idx);}
307 using value_type = T;
308 using difference_type = int;
309 using reference = T&;
310 using iterator_category = std::forward_iterator_tag;
318 if (itr->size() > 0ul)
319 value_ = detail::tag_invoke(detail::convert_row_tag<T>{}, *itr);
324 return itr_ != rhs.itr_;
327 value_type &operator*() {
return value_; }
328 value_type *operator->() {
return &value_; }
333 if (itr_->size() > 0ul)
334 value_ = detail::tag_invoke(detail::convert_row_tag<T>{}, *itr_);
358 void check_columns_( system::error_code & ec,
error_info & ei)
360 detail::tag_invoke(detail::check_columns_tag<T>{}, result_, ec, ei);
364BOOST_SQLITE_END_NAMESPACE
main object for a connection to a database.
Additional information about error conditions stored in an sqlite-allocate string.
The input iterator can be used to read every row in a for-loop.
Representation of a result from a database.
A statement used for a prepared-statement.
The input iterator can be used to read every row in a for-loop.
A typed resultset using a tuple or a described struct.
core::string_view column_name(std::size_t idx) const
Returns the name of the column idx.
bool read_next(system::error_code &ec, error_info &ei)
T current() const &
Returns the current row.
iterator begin()
Return an input iterator to the currently unread row.
core::string_view column_origin_name(std::size_t idx) const
Returns the origin name of the column for column idx.
bool done() const
Checks if the last row has been reached.
iterator end()
Sentinel iterator.
core::string_view table_name(std::size_t idx) const
Returns the name of the source table for column idx.