summaryrefslogtreecommitdiff
path: root/include/boost/sqlite/static_resultset.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'include/boost/sqlite/static_resultset.hpp')
-rw-r--r--include/boost/sqlite/static_resultset.hpp497
1 files changed, 0 insertions, 497 deletions
diff --git a/include/boost/sqlite/static_resultset.hpp b/include/boost/sqlite/static_resultset.hpp
deleted file mode 100644
index fd6c359..0000000
--- a/include/boost/sqlite/static_resultset.hpp
+++ /dev/null
@@ -1,497 +0,0 @@
-//
-// Copyright (c) 2024 Klemens Morgenstern (klemens.morgenstern@gmx.net)
-//
-// 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_SQLITE_STATIC_RESULTSET_HPP
-#define BOOST_SQLITE_STATIC_RESULTSET_HPP
-
-#include <boost/sqlite/detail/config.hpp>
-#include <boost/sqlite/resultset.hpp>
-#include <boost/sqlite/connection.hpp>
-
-#include <boost/describe/members.hpp>
-
-#include <array>
-#include <cstdint>
-
-#if __cplusplus >= 202002L
-#include <boost/pfr/core.hpp>
-#include <boost/pfr/core_name.hpp>
-#include <boost/pfr/traits.hpp>
-#endif
-
-
-namespace boost { template<typename> class optional;}
-
-#if __cplusplus >= 201702L
-#include <optional>
-#endif
-
-BOOST_SQLITE_BEGIN_NAMESPACE
-
-namespace detail
-{
-
-inline void convert_field(sqlite_int64 & target, const field & f) {target = f.get_int();}
-
-template<typename = std::enable_if_t<!std::is_same<std::int64_t, sqlite_int64>::value>>
-inline void convert_field(std::int64_t & target, const field & f)
-{
- target = static_cast<std::int64_t>(f.get_int());
-}
-
-inline void convert_field(double & target, const field & f) {target = f.get_double();}
-
-
-template<typename Traits = std::char_traits<char>, typename Allocator = std::allocator<char>>
-inline void convert_field(std::basic_string<char, Traits, Allocator> & target, const field & f)
-{
- auto t = f.get_text();
- target.assign(t.begin(), t.end());
-}
-
-inline void convert_field(string_view & target, const field & f) {target = f.get_text();}
-inline void convert_field(blob & target, const field & f) {target = blob(f.get_blob());}
-inline void convert_field(blob_view & target, const field & f) {target = f.get_blob();}
-
-#if __cplusplus >= 201702L
-template<typename T>
-inline void convert_field(std::optional<T> & target, const field & f)
-{
- if (f.is_null())
- target.reset();
- else
- convert_field(target.emplace(), f);
-}
-#endif
-
-template<typename T>
-inline void convert_field(boost::optional<T> & target, const field & f)
-{
- if (f.is_null())
- target.reset();
- else
- return convert_field(target.emplace_back(), f);
-}
-
-template<typename T>
-inline constexpr bool field_type_is_nullable(const T& ) {return false;}
-#if __cplusplus >= 201702L
-template<typename T>
-inline bool field_type_is_nullable(const std::optional<T> &) { return true; }
-#endif
-template<typename T>
-inline bool field_type_is_nullable(const boost::optional<T> &) { return true; }
-
-inline value_type required_field_type(const sqlite3_int64 &) {return value_type::integer;}
-
-template<typename = std::enable_if_t<!std::is_same<std::int64_t, sqlite_int64>::value>>
-inline value_type required_field_type(const std::int64_t &) {return value_type::integer;}
-
-template<typename Allocator, typename Traits>
-inline value_type required_field_type(const std::basic_string<char, Allocator, Traits> & )
-{
- return value_type::text;
-}
-
-inline value_type required_field_type(const string_view &) {return value_type::text;}
-inline value_type required_field_type(const blob &) {return value_type::blob;}
-inline value_type required_field_type(const blob_view &) {return value_type::blob;}
-
-
-template<typename ... Args>
-void check_columns(const std::tuple<Args...> *, const resultset & r,
- system::error_code &ec, error_info & ei)
-{
- if (r.column_count() != sizeof...(Args))
- {
- BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
- ei.format("Tuple size doesn't match column count [%ld != %ld]", sizeof...(Args), r.column_count());
- }
-}
-
-template<bool Strict, typename ... Args>
-void convert_row(std::tuple<Args...> & res, const row & r, system::error_code ec, error_info & ei)
-{
- std::size_t idx = 0u;
-
- mp11::tuple_for_each(
- res,
- [&](auto & v)
- {
- const auto i = idx++;
- const auto & f = r[i];
- BOOST_IF_CONSTEXPR (Strict)
- {
- if (!ec) // only check if we don't have an error yet.
- {
- if (f.is_null() && !field_type_is_nullable(v))
- {
- BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_CONSTRAINT_NOTNULL);
- ei.format("unexpected null in column %d", i);
- }
- else if (f.type() != required_field_type(v))
- {
- BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_CONSTRAINT_DATATYPE);
- ei.format("unexpected type [%s] in column %d, expected [%s]",
- value_type_name(f.type()), i, value_type_name(required_field_type(v)));
- }
- }
- }
- else
- boost::ignore_unused(ec, ei);
-
- detail::convert_field(v, f);
- });
-}
-
-#if defined(BOOST_DESCRIBE_CXX14)
-
-template<typename T, typename = typename std::enable_if<describe::has_describe_members<T>::value>::type>
-void check_columns(const T *, const resultset & r,
- system::error_code &ec, error_info & ei)
-{
- using mems = boost::describe::describe_members<T, describe::mod_public>;
- constexpr std::size_t sz = mp11::mp_size<mems>();
- if (r.column_count() != sz)
- {
- BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
- ei.format("Describe size doesn't match column count [%ld != %ld]", sz, r.column_count());
- }
-
- // columns can be duplicated!
- std::array<bool, sz> found;
- std::fill(found.begin(), found.end(), false);
-
- for (std::size_t i = 0ul; i < r.column_count(); i++)
- {
- bool cfound = false;
- boost::mp11::mp_for_each<mp11::mp_iota_c<sz>>(
- [&](auto sz)
- {
- auto d = mp11::mp_at_c<mems, sz>();
- if (d.name == r.column_name(i))
- {
- found[sz] = true;
- cfound = true;
- }
- });
-
- if (!cfound)
- {
- BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
- ei.format("Column '%s' not found in described struct.", r.column_name(i).c_str());
- break;
- }
- }
-
- if (ec)
- return;
-
-
- auto itr = std::find(found.begin(), found.end(), false);
- if (itr != found.end())
- {
- mp11::mp_with_index<sz>(
- std::distance(found.begin(), itr),
- [&](auto sz)
- {
- auto d = mp11::mp_at_c<mems, sz>();
- BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
- ei.format("Described field '%s' not found in resultset struct.", d.name);
- });
- }
-}
-
-template<bool Strict, typename T,
- typename = typename std::enable_if<describe::has_describe_members<T>::value>::type>
-void convert_row(T & res, const row & r, system::error_code ec, error_info & ei)
-{
- for (auto && f: r)
- {
- boost::mp11::mp_for_each<boost::describe::describe_members<T, describe::mod_public> >(
- [&](auto D)
- {
- if (D.name == f.column_name())
- {
- auto & r = res.*D.pointer;
- BOOST_IF_CONSTEXPR(Strict)
- {
- if (!ec)
- {
- if (f.is_null() && !field_type_is_nullable(r))
- {
- BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_CONSTRAINT_NOTNULL);
- ei.format("unexpected null in column %s", D.name);
- }
- else if (f.type() != required_field_type(r))
- {
- BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_CONSTRAINT_DATATYPE);
- ei.format("unexpected type [%s] in column %s, expected [%s]",
- value_type_name(f.type()), D.name, value_type_name(required_field_type(r)));
- }
- }
- }
-
- detail::convert_field(r, f);
- }
- });
- }
-}
-
-#endif
-
-#if __cplusplus >= 202002L
-
-template<typename T>
- requires (std::is_aggregate_v<T> && !describe::has_describe_members<T>::value)
-void check_columns(const T *, const resultset & r,
- system::error_code &ec, error_info & ei)
-{
- constexpr std::size_t sz = pfr::tuple_size_v<T>;
- if (r.column_count() != sz)
- {
- BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
- ei.format("Describe size doesn't match column count [%ld != %ld]", sz, r.column_count());
- }
-
- // columns can be duplicated!
- std::array<bool, sz> found;
- std::fill(found.begin(), found.end(), false);
-
- for (std::size_t i = 0ul; i < r.column_count(); i++)
- {
- bool cfound = false;
- boost::mp11::mp_for_each<mp11::mp_iota_c<sz>>(
- [&](auto sz)
- {
- if (pfr::get_name<sz, T>() == r.column_name(i))
- {
- found[sz] = true;
- cfound = true;
- }
- });
-
- if (!cfound)
- {
- BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
- ei.format("Column %s not found in struct.", r.column_name(i).c_str());
- break;
- }
- }
-
- if (ec)
- return;
-
-
- auto itr = std::find(found.begin(), found.end(), false);
- if (itr != found.end())
- {
- mp11::mp_with_index<sz>(
- std::distance(found.begin(), itr),
- [&](auto sz)
- {
- BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
- auto nm = pfr::get_name<sz, T>();
- ei.format("PFR field %.*s not found in resultset struct.", static_cast<int>(nm.size()), nm.data());
- });
- }
-}
-
-template<bool Strict, typename T>
- requires (std::is_aggregate_v<T> && !describe::has_describe_members<T>::value)
-void convert_row(T & res, const row & r, system::error_code ec, error_info & ei)
-{
- for (auto && f: r)
- {
- boost::mp11::mp_for_each<mp11::mp_iota_c<pfr::tuple_size_v<T>>>(
- [&](auto D)
- {
- if (pfr::get_name<D, T>() == f.column_name().c_str())
- {
- auto & r = pfr::get<D()>(res);
- BOOST_IF_CONSTEXPR(Strict)
- {
- if (!ec)
- {
- if (f.is_null() && !field_type_is_nullable(r))
- {
- BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_CONSTRAINT_NOTNULL);
- ei.format("unexpected null in column %s", D.name);
- }
- else if (f.type() != required_field_type(r))
- {
- BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_CONSTRAINT_DATATYPE);
- ei.format("unexpected type [%s] in column %s, expected [%s]",
- value_type_name(f.type()), D.name, value_type_name(required_field_type(r)));
- }
- }
- }
- detail::convert_field(r, f);
- }
- });
- }
-}
-
-#endif
-
-}
-
-/**
- @brief A typed resultset using a tuple or a described struct.
- @ingroup reference
- @tparam T The static type of the query.
- @tparam Strict Disables implicit conversions.
-
- If is a forward-range with output iterators.
-
- @par Example
-
- @code{.cpp}
-
- extern sqlite::connection conn;
- struct user { std::string first_name; std::string last_name; };
- BOOST_DESCRIBE_STRUCT(user, (), (first_name, last_name));
-
- sqlite::resultset rs = conn.query("select first_name, last_name from users;");
-
- do
- {
- user usr = r.current();
- handle_row(u);
- }
- while (rs.read_next()) // read it line by line
-
- @endcode
-
-*/
-template<typename T, bool Strict >
-struct static_resultset
-{
- /// Returns the current row.
- T current() const &
- {
- system::error_code ec;
- error_info ei;
- auto tmp = current(ec, ei);
- if (ec)
- throw_exception(system::system_error(ec, ei.message()));
- return tmp;
- }
-
- /// Returns the current row.
- T current(system::error_code & ec, error_info & ei) const &
- {
- T res;
- detail::convert_row<Strict>(res, result_.current(), ec, ei);
- return res;
- }
-
- /// Checks if the last row has been reached.
- bool done() const {return result_.done();}
-
- ///@{
- /// Read the next row. Returns false if there's nothing more to read.
- BOOST_SQLITE_DECL bool read_next(system::error_code & ec, error_info & ei) { return result_.read_next(ec, ei); }
- BOOST_SQLITE_DECL bool read_next() { return result_.read_next(); }
- ///@}
-
- ///
- std::size_t column_count() const { return result_.column_count(); }
- /// Returns the name of the column idx.
- core::string_view column_name(std::size_t idx) const { return result_.column_name(idx); }
-
- /// Returns the name of the source table for column idx.
- core::string_view table_name(std::size_t idx) const { return result_.table_name(idx);}
- /// Returns the origin name of the column for column idx.
- core::string_view column_origin_name(std::size_t idx) const { return result_.column_origin_name(idx);}
-
- static_resultset() = default;
- static_resultset(resultset && result) : result_(std::move(result)) { }
-
-
- static_resultset(static_resultset<T, false> && rhs) : result_(std::move(rhs.result_)) {}
-
- /// The input iterator can be used to read every row in a for-loop
- struct iterator
- {
- using value_type = T;
- using difference_type = int;
- using reference = T&;
- using iterator_category = std::forward_iterator_tag;
-
- iterator()
- {
-
- }
- explicit iterator(resultset::iterator itr) : itr_(itr)
- {
- system::error_code ec;
- error_info ei;
- if (itr->size() > 0ul)
- detail::convert_row<Strict>(value_, *itr, ec, ei);
- if (ec)
- throw_exception(system::system_error(ec, ei.message()));
- }
-
- bool operator!=(iterator rhs) const
- {
- return itr_ != rhs.itr_;
- }
-
- value_type &operator*() { return value_; }
- value_type *operator->() { return &value_; }
-
- iterator& operator++()
- {
- ++itr_;
-
- system::error_code ec;
- error_info ei;
- if (itr_->size() > 0ul)
- detail::convert_row<Strict>(value_, *itr_, ec, ei);
- if (ec)
- throw_exception(system::system_error(ec, ei.message()));
-
- return *this;
- }
- iterator operator++(int)
- {
- auto l = *this;
- ++(*this);
- return l;
- }
- private:
- resultset::iterator itr_;
- value_type value_;
- };
-
- /// Return an input iterator to the currently unread row
- iterator begin() { return iterator(result_.begin());}
- /// Sentinel iterator.
- iterator end() { return iterator(result_.end()); }
-
-
-
- static_resultset<T, true> strict() &&
- {
- return {std::move(result_)};
- }
- private:
-
- friend struct static_resultset<T, true>;
- friend struct connection;
- friend struct statement;
- resultset result_;
- void check_columns_( system::error_code & ec, error_info & ei)
- {
- detail::check_columns(static_cast<T*>(nullptr), result_, ec, ei);
- }
-};
-
-BOOST_SQLITE_END_NAMESPACE
-
-#endif //BOOST_SQLITE_STATIC_RESULTSET_HPP