userver: userver/storages/postgres/typed_result_set.hpp Source File
Loading...
Searching...
No Matches
typed_result_set.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/postgres/typed_result_set.hpp
4/// @brief Typed PostgreSQL results
5
6#include <type_traits>
7
8#include <userver/storages/postgres/detail/typed_rows.hpp>
9#include <userver/storages/postgres/result_set.hpp>
10
11USERVER_NAMESPACE_BEGIN
12
13namespace storages::postgres {
14
15/// @page pg_user_row_types uPg: Typed PostgreSQL results
16///
17/// The ResultSet provides access to a generic PostgreSQL result buffer wrapper
18/// with access to individual column buffers and means to parse the buffers into
19/// a certain type.
20///
21/// For a user that wishes to get the results in a form of a sequence or a
22/// container of C++ tuples or structures, the driver provides a way to coerce
23/// the generic result set into a typed result set or a container of tuples or
24/// structures that fulfill certain conditions.
25///
26/// TypedResultSet provides container interface for typed result rows for
27/// iteration or random access without converting all the result set at once.
28/// The iterators in the TypedResultSet satisfy requirements for a constant
29/// RandomAccessIterator with the exception of dereferencing iterators.
30///
31/// @warning The operator* of the iterators returns value (not a reference to
32/// it) and the iterators don't have the operator->.
33///
34/// @par Data row extraction
35///
36/// The data rows can be obtained as:
37/// - std::tuple;
38/// - aggregate class as is;
39/// - non-aggregate class with some augmentation.
40///
41/// Data members of the tuple or the classes must be supported by the driver.
42/// For more information on supported data types please see
43/// @ref scripts/docs/en/userver/pg_types.md.
44///
45/// @par std::tuple.
46///
47/// The first option is to convert ResultSet's row to std::tuples.
48///
49/// ```
50/// using MyRowType = std::tuple<int, string>;
51/// auto trx = ...;
52/// auto generic_result = trx.Execute("select a, b from my_table");
53/// auto iteration = generic_result.AsSetOf<MyRowType>();
54/// for (auto row : iteration) {
55/// static_assert(std::is_same_v<decltype(row), MyRowType>,
56/// "Iterate over tuples");
57/// auto [a, b] = row;
58/// std::cout << "a = " << a << "; b = " << b << "\n";
59/// }
60///
61/// auto data = geric_result.AsContainer<std::vector<MyRowType>>();
62/// ```
63///
64/// @par Aggregate classes.
65///
66/// A data row can be coerced to an aggregate class.
67///
68/// An aggregate class (C++03 8.5.1 §1) is a class that with no base classes, no
69/// protected or private non-static data members, no user-declared constructors
70/// and no virtual functions.
71///
72/// ```
73/// struct MyRowType {
74/// int a;
75/// std::string b;
76/// };
77/// auto generic_result = trx.Execute("select a, b from my_table");
78/// auto iteration = generic_result.AsSetOf<MyRowType>();
79/// for (auto row : iteration) {
80/// static_assert(std::is_same_v<decltype(row), MyRowType>,
81/// "Iterate over aggregate classes");
82/// std::cout << "a = " << row.a << "; b = " << row.b << "\n";
83/// }
84///
85/// auto data = geric_result.AsContainer<std::vector<MyRowType>>();
86/// ```
87///
88/// @par Non-aggregate classes.
89///
90/// Classes that do not satisfy the aggregate class requirements can be used
91/// to be created from data rows by providing additional `Introspect` non-static
92/// member function. The function should return a tuple of references to
93/// member data fields. The class must be default constructible.
94///
95/// ```
96/// class MyRowType {
97/// private:
98/// int a_;
99/// std::string b_;
100/// public:
101/// MyRowType() = default; // default ctor is required
102/// explicit MyRowType(int x);
103///
104/// auto Introspect() {
105/// return std::tie(a_, b_);
106/// }
107/// int GetA() const;
108/// const std::string& GetB() const;
109/// };
110///
111/// auto generic_result = trx.Execute("select a, b from my_table");
112/// auto iteration = generic_result.AsSetOf<MyRowType>();
113/// for (auto row : iteration) {
114/// static_assert(std::is_same_v<decltype(row), MyRowType>,
115/// "Iterate over non-aggregate classes");
116/// std::cout << "a = " << row.GetA() << "; b = " << row.GetB() << "\n";
117/// }
118///
119/// auto data = geric_result.AsContainer<std::vector<MyRowType>>();
120/// ```
121/// @par Single-column result set
122///
123/// A single-column result set can be used to extract directly to the column
124/// type. User types mapped to PostgreSQL will work as well. If you need to
125/// extract the whole row into such a structure, you will need to disambiguate
126/// the call with the kRowTag.
127///
128/// @code
129/// auto string_set = generic_result.AsSetOf<std::string>();
130/// std::string s = string_set[0];
131///
132/// auto string_vec = generic_result.AsContainer<std::vector<std::string>>();
133///
134/// // Extract first column into the composite type
135/// auto foo_set = generic_result.AsSetOf<FooBar>();
136/// auto foo_vec = generic_result.AsContainer<std::vector<FooBar>>();
137///
138/// // Extract the whole row, disambiguation
139/// auto foo_set = generic_result.AsSetOf<FooBar>(kRowTag);
140///
141/// @endcode
142///
143///
144/// ----------
145///
146/// @htmlonly <div class="bottom-nav"> @endhtmlonly
147/// ⇦ @ref scripts/docs/en/userver/pg_types.md | @ref pg_errors ⇨
148/// @htmlonly </div> @endhtmlonly
149
150template <typename T, typename ExtractionTag>
151class TypedResultSet {
152public:
153 using size_type = ResultSet::size_type;
154 using difference_type = ResultSet::difference_type;
155 static constexpr size_type npos = ResultSet::npos;
156 static constexpr ExtractionTag kExtractTag{};
157
158 //@{
159 /** @name Row container concept */
160 using const_iterator = detail::ConstTypedRowIterator<T, ExtractionTag, detail::IteratorDirection::kForward>;
161 using const_reverse_iterator = detail::ConstTypedRowIterator<T, ExtractionTag, detail::IteratorDirection::kReverse>;
162
163 using value_type = T;
164 using pointer = const_iterator;
165
166// Forbidding assignments to operator[] result in debug, getting max
167// performance in release.
168#ifdef NDEBUG
169 using reference = value_type;
170#else
171 using reference = std::add_const_t<value_type>;
172#endif
173
174 //@}
175 explicit TypedResultSet(ResultSet result) : result_{std::move(result)} {}
176
177 /// Number of rows in the result set
178 size_type Size() const { return result_.Size(); }
179 bool IsEmpty() const { return Size() == 0; }
180 //@{
181 /** @name Container interface */
182 //@{
183 /** @name Row container interface */
184 //@{
185 /** @name Forward iteration */
186 const_iterator cbegin() const& { return const_iterator{result_.pimpl_, 0}; }
187 const_iterator begin() const& { return cbegin(); }
188 const_iterator cend() const& { return const_iterator{result_.pimpl_, Size()}; }
189 const_iterator end() const& { return cend(); }
190 const_iterator cbegin() const&& { ReportMisuse(); }
191 const_iterator begin() const&& { ReportMisuse(); }
192 const_iterator cend() const&& { ReportMisuse(); }
193 const_iterator end() const&& { ReportMisuse(); }
194 //@}
195 //@{
196 /** @name Reverse iteration */
197 const_reverse_iterator crbegin() const& { return const_reverse_iterator(result_.pimpl_, Size() - 1); }
198 const_reverse_iterator rbegin() const& { return crbegin(); }
199 const_reverse_iterator crend() const& { return const_reverse_iterator(result_.pimpl_, npos); }
200 const_reverse_iterator rend() const& { return crend(); }
201 const_reverse_iterator crbegin() const&& { ReportMisuse(); }
202 const_reverse_iterator rbegin() const&& { ReportMisuse(); }
203 const_reverse_iterator crend() const&& { ReportMisuse(); }
204 const_reverse_iterator rend() const&& { ReportMisuse(); }
205 //@}
206 /// @brief Access a row by index
207 /// @throws RowIndexOutOfBounds if index is out of bounds
208 // NOLINTNEXTLINE(readability-const-return-type)
209 reference operator[](size_type index) const& { return result_[index].template As<value_type>(kExtractTag); }
210 // NOLINTNEXTLINE(readability-const-return-type)
211 reference operator[](size_type) const&& { ReportMisuse(); }
212 //@}
213private:
214 [[noreturn]] static void ReportMisuse() {
215 static_assert(!sizeof(T), "keep the TypedResultSet before using, please");
216 }
217
218 ResultSet result_;
219};
220
221} // namespace storages::postgres
222
223USERVER_NAMESPACE_END