boost_sqlite 1
A sqlite C++ library
Loading...
Searching...
No Matches
static_resultset.hpp
1//
2// Copyright (c) 2024 Klemens Morgenstern (klemens.morgenstern@gmx.net)
3//
4// Distributed under the Boost Software License, Version 1.0. (See accompanying
5// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6//
7
8#ifndef BOOST_SQLITE_STATIC_RESULTSET_HPP
9#define BOOST_SQLITE_STATIC_RESULTSET_HPP
10
11#include <boost/sqlite/detail/config.hpp>
12#include <boost/sqlite/resultset.hpp>
13#include <boost/sqlite/connection.hpp>
14
15#include <boost/describe/members.hpp>
16
17#include <array>
18
19#if __cplusplus >= 202002L
20#include <boost/pfr/core.hpp>
21#include <boost/pfr/core_name.hpp>
22#include <boost/pfr/traits.hpp>
23#endif
24
25BOOST_SQLITE_BEGIN_NAMESPACE
26
27namespace detail
28{
29
30template<typename T>
31struct value_to_tag {};
32
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();}
37
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)
41{
42 return f.get_text();
43}
44
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();}
48
49
50template<typename>
51struct check_columns_tag {};
52
53
54template<typename>
55struct convert_row_tag {};
56
57template<typename ... Args>
58void tag_invoke(check_columns_tag<std::tuple<Args...>>, const resultset & r,
59 system::error_code &ec, error_info & ei)
60{
61 if (r.column_count() != sizeof...(Args))
62 {
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());
65 }
66}
67
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...>)
71{
72 return Tuple{ tag_invoke(value_to_tag<typename std::tuple_element<Ns, Tuple>::type>{}, r[Ns])... };
73
74}
75
76
77template<typename ... Args>
78std::tuple<Args...> tag_invoke(convert_row_tag<std::tuple<Args...>> tag, const row & r)
79{
80 return convert_row_to_tuple_impl(tag, r, mp11::make_index_sequence<sizeof...(Args)>{});
81}
82
83#if defined(BOOST_DESCRIBE_CXX14)
84
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)
88{
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)
92 {
93 BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
94 ei.format("Describe size doesn't match column count [%d != %d]", sz, r.column_count());
95 }
96
97 // columns can be duplicated!
98 std::array<bool, sz> found;
99 std::fill(found.begin(), found.end(), false);
100
101 for (std::size_t i = 0ul; i < r.column_count(); i++)
102 {
103 bool cfound = false;
104 boost::mp11::mp_for_each<mp11::mp_iota_c<sz>>(
105 [&](auto sz)
106 {
107 auto d = mp11::mp_at_c<mems, sz>();
108 if (d.name == r.column_name(i))
109 {
110 found[sz] = true;
111 cfound = true;
112 }
113 });
114
115 if (!cfound)
116 {
117 BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
118 ei.format("Column %Q not found in described struct.", r.column_name(i));
119 break;
120 }
121 }
122
123 if (ec)
124 return;
125
126
127 auto itr = std::find(found.begin(), found.end(), false);
128 if (itr != found.end())
129 {
130 mp11::mp_with_index<sz>(
131 std::distance(found.begin(), itr),
132 [&](auto sz)
133 {
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);
137 });
138 }
139}
140
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)
143{
144 T res;
145 for (auto && c: r)
146 {
147 boost::mp11::mp_for_each<boost::describe::describe_members<T, describe::mod_public> >(
148 [&](auto D)
149 {
150 if (D.name == c.column_name())
151 {
152 auto & r = res.*D.pointer;
153 r = tag_invoke(value_to_tag<typename std::decay<decltype(r)>::type>{}, c);
154 }
155 });
156 }
157 return res;
158}
159
160#endif
161
162#if __cplusplus >= 202002L
163
164template<typename T>
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)
168{
169 constexpr std::size_t sz = pfr::tuple_size_v<T>;
170 if (r.column_count() != sz)
171 {
172 BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
173 ei.format("Describe size doesn't match column count [%d != %d]", sz, r.column_count());
174 }
175
176 // columns can be duplicated!
177 std::array<bool, sz> found;
178 std::fill(found.begin(), found.end(), false);
179
180 for (std::size_t i = 0ul; i < r.column_count(); i++)
181 {
182 bool cfound = false;
183 boost::mp11::mp_for_each<mp11::mp_iota_c<sz>>(
184 [&](auto sz)
185 {
186 if (pfr::get_name<sz, T>() == r.column_name(i))
187 {
188 found[sz] = true;
189 cfound = true;
190 }
191 });
192
193 if (!cfound)
194 {
195 BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
196 ei.format("Column %Q not found in struct.", r.column_name(i));
197 break;
198 }
199 }
200
201 if (ec)
202 return;
203
204
205 auto itr = std::find(found.begin(), found.end(), false);
206 if (itr != found.end())
207 {
208 mp11::mp_with_index<sz>(
209 std::distance(found.begin(), itr),
210 [&](auto sz)
211 {
212 BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
213 ei.format("PFR field %Q not found in resultset struct.", pfr::get_name<sz, T>() );
214 });
215 }
216}
217
218template<typename 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)
221{
222 T res;
223 for (auto && c: r)
224 {
225 boost::mp11::mp_for_each<mp11::mp_iota_c<pfr::tuple_size_v<T>>>(
226 [&](auto D)
227 {
228
229 if (pfr::get_name<D, T>() == c.column_name().c_str())
230 {
231 auto & r = pfr::get<D()>(res);
232 r = tag_invoke(value_to_tag<std::decay_t<decltype(r)>>{}, c);
233 }
234 });
235 }
236 return res;
237}
238
239#endif
240
241}
242
271template<typename T>
273{
275 T current() const &
276 {
277 return detail::tag_invoke(detail::convert_row_tag<T>{}, result_.current());
278 }
280 bool done() const {return result_.done();}
281
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(); }
287
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); }
292
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);}
297
298 static_resultset() = default;
299 static_resultset(resultset && result) : result_(std::move(result))
300 {
301 }
302
303
305 struct iterator
306 {
307 using value_type = T;
308 using difference_type = int;
309 using reference = T&;
310 using iterator_category = std::forward_iterator_tag;
311
312 iterator()
313 {
314
315 }
316 explicit iterator(resultset::iterator itr) : itr_(itr)
317 {
318 if (itr->size() > 0ul)
319 value_ = detail::tag_invoke(detail::convert_row_tag<T>{}, *itr);
320 }
321
322 bool operator!=(iterator rhs) const
323 {
324 return itr_ != rhs.itr_;
325 }
326
327 value_type &operator*() { return value_; }
328 value_type *operator->() { return &value_; }
329
330 iterator& operator++()
331 {
332 ++itr_;
333 if (itr_->size() > 0ul)
334 value_ = detail::tag_invoke(detail::convert_row_tag<T>{}, *itr_);
335 return *this;
336 }
337 iterator operator++(int)
338 {
339 auto l = *this;
340 ++(*this);
341 return l;
342 }
343 private:
345 value_type value_;
346 };
347
349 iterator begin() { return iterator(result_.begin());}
351 iterator end() { return iterator(result_.end()); }
352
353
354 private:
355 friend struct connection;
356 friend struct statement;
357 resultset result_;
358 void check_columns_( system::error_code & ec, error_info & ei)
359 {
360 detail::tag_invoke(detail::check_columns_tag<T>{}, result_, ec, ei);
361 }
362};
363
364BOOST_SQLITE_END_NAMESPACE
365
366#endif //BOOST_SQLITE_STATIC_RESULTSET_HPP
@ blob
A binary value.
main object for a connection to a database.
Additional information about error conditions stored in an sqlite-allocate string.
Definition error.hpp:33
The input iterator can be used to read every row in a for-loop.
Definition resultset.hpp:81
Representation of a result from a database.
Definition resultset.hpp:41
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.