16#include <boost/pfr/core.hpp>
17#include <boost/pfr/traits.hpp>
19#include <userver/storages/odbc/exception.hpp>
20#include <userver/storages/odbc/odbc_fwd.hpp>
21#include <userver/storages/odbc/row.hpp>
23USERVER_NAMESPACE_BEGIN
25namespace storages::
odbc {
30struct OdbcResultMappingTag;
32template <
typename T, std::size_t... Index>
33constexpr bool AreResultMembersMappable(std::index_sequence<Index...>) {
34 return sizeof...(Index) != 0 &&
35 ((!std::is_reference_v<boost::pfr::tuple_element_t<Index, T>> &&
36 kIsFieldAsType<std::remove_cv_t<boost::pfr::tuple_element_t<Index, T>>>) &&
41constexpr bool DetectResultAggregate() {
42 using Value = std::remove_cv_t<T>;
43 if constexpr (std::is_class_v<Value> && std::is_aggregate_v<Value> && std::is_standard_layout_v<Value> &&
44 !std::is_union_v<Value> && io::traits::kAggregateHasNoBaseClass<Value> &&
45 boost::pfr::is_implicitly_reflectable_v<Value, OdbcResultMappingTag> && !kIsFieldAsType<Value> &&
46 !io::traits::kHasMappingDeclaration<Value>)
48 return AreResultMembersMappable<Value>(std::make_index_sequence<boost::pfr::tuple_size_v<Value>>{});
55inline constexpr bool kIsResultAggregate = DetectResultAggregate<T>();
58inline constexpr bool kIsResultValue = kIsFieldAsType<std::remove_cv_t<T>> || kIsResultAggregate<T>;
60template <
typename Container>
61concept ResultContainer =
62 std::default_initializable<Container> && !std::same_as<std::remove_cv_t<Container>, std::string> &&
63 requires {
typename Container::value_type; } && kIsResultValue<
typename Container::value_type> &&
64 requires(Container& container,
typename Container::value_type value) {
65 container.insert(container.end(), std::move(value));
72class ResultSet
final {
74 using size_type = std::size_t;
75 using difference_type = std::ptrdiff_t;
76 static constexpr size_type npos = std::numeric_limits<size_type>::max();
81 using value_type =
Row;
82 using reference = value_type;
85 explicit ResultSet(std::shared_ptr<detail::ResultWrapper> pimpl)
86 : pimpl_{std::move(pimpl)}
92 size_type Size()
const;
104 reference operator[](size_type index)
const&;
109 template <
typename Container>
110 requires impl::ResultContainer<Container>
114 template <
typename T>
115 requires impl::kIsResultValue<T>
121 template <
typename T>
122 requires impl::kIsResultValue<T>
126 template <
typename T, std::size_t... Index>
127 T MapAggregate(size_type row_index, std::index_sequence<Index...>)
const;
129 template <
typename T>
130 T MapRow(size_type row_index)
const;
132 std::shared_ptr<detail::ResultWrapper> pimpl_;
135template <
typename T, std::size_t... Index>
136T ResultSet::MapAggregate(size_type row_index, std::index_sequence<Index...>)
const {
138 operator[](row_index)[Index]
139 .
template As<std::remove_cvref_t<
decltype(boost::pfr::get<Index>(std::declval<T&>()))>>()...
144T ResultSet::MapRow(size_type row_index)
const {
145 using Value = std::remove_cv_t<T>;
146 static_assert(impl::kIsResultValue<Value>,
"Unsupported ODBC typed result value");
148 if constexpr (impl::kIsFieldAsType<Value>) {
150 throw ResultSetError(
"ODBC scalar result mapping requires exactly one column");
152 return operator[](row_index)[0].
template As<Value>();
154 constexpr auto kFieldCount = boost::pfr::tuple_size_v<Value>;
156 throw ResultSetError(
"ODBC aggregate result mapping requires exactly one column per aggregate member");
158 return MapAggregate<Value>(row_index, std::make_index_sequence<kFieldCount>{});
162template <
typename Container>
163requires impl::ResultContainer<Container>
165 using Value =
typename Container::value_type;
166 static_assert(impl::kIsResultValue<Value>,
"Unsupported ODBC typed result container value");
169 if constexpr (
requires { result.reserve(Size()); }) {
170 result.reserve(Size());
172 auto output = std::inserter(result, result.end());
173 for (size_type index = 0; index < Size(); ++index) {
174 *output++ = MapRow<Value>(index);
180requires impl::kIsResultValue<T>
183 throw ResultSetError(
"ODBC single-row result mapping requires exactly one row");
189requires impl::kIsResultValue<T>
192 throw ResultSetError(
"ODBC optional single-row result mapping accepts at most one row");
197 return std::optional<T>{MapRow<T>(0)};