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 @ref pg_types
43///
44/// @par std::tuple.
45///
46/// The first option is to convert ResultSet's row to std::tuples.
47///
48/// ```
49/// using MyRowType = std::tuple<int, string>;
50/// auto trx = ...;
51/// auto generic_result = trx.Execute("select a, b from my_table");
52/// auto iteration = generic_result.AsSetOf<MyRowType>();
53/// for (auto row : iteration) {
54/// static_assert(std::is_same_v<decltype(row), MyRowType>,
55/// "Iterate over tuples");
56/// auto [a, b] = row;
57/// std::cout << "a = " << a << "; b = " << b << "\n";
58/// }
59///
60/// auto data = geric_result.AsContainer<std::vector<MyRowType>>();
61/// ```
62///
63/// @par Aggregate classes.
64///
65/// A data row can be coerced to an aggregate class.
66///
67/// An aggregate class (C++03 8.5.1 §1) is a class that with no base classes, no
68/// protected or private non-static data members, no user-declared constructors
69/// and no virtual functions.
70///
71/// ```
72/// struct MyRowType {
73/// int a;
74/// std::string b;
75/// };
76/// auto generic_result = trx.Execute("select a, b from my_table");
77/// auto iteration = generic_result.AsSetOf<MyRowType>();
78/// for (auto row : iteration) {
79/// static_assert(std::is_same_v<decltype(row), MyRowType>,
80/// "Iterate over aggregate classes");
81/// std::cout << "a = " << row.a << "; b = " << row.b << "\n";
82/// }
83///
84/// auto data = geric_result.AsContainer<std::vector<MyRowType>>();
85/// ```
86///
87/// @par Non-aggregate classes.
88///
89/// Classes that do not satisfy the aggregate class requirements can be used
90/// to be created from data rows by providing additional `Introspect` non-static
91/// member function. The function should return a tuple of references to
92/// member data fields. The class must be default constructible.
93///
94/// ```
95/// class MyRowType {
96/// private:
97/// int a_;
98/// std::string b_;
99/// public:
100/// MyRowType() = default; // default ctor is required
101/// explicit MyRowType(int x);
102///
103/// auto Introspect() {
104/// return std::tie(a_, b_);
105/// }
106/// int GetA() const;
107/// const std::string& GetB() const;
108/// };
109///
110/// auto generic_result = trx.Execute("select a, b from my_table");
111/// auto iteration = generic_result.AsSetOf<MyRowType>();
112/// for (auto row : iteration) {
113/// static_assert(std::is_same_v<decltype(row), MyRowType>,
114/// "Iterate over non-aggregate classes");
115/// std::cout << "a = " << row.GetA() << "; b = " << row.GetB() << "\n";
116/// }
117///
118/// auto data = geric_result.AsContainer<std::vector<MyRowType>>();
119/// ```
120/// @par Single-column result set
121///
122/// A single-column result set can be used to extract directly to the column
123/// type. User types mapped to PostgreSQL will work as well. If you need to
124/// extract the whole row into such a structure, you will need to disambiguate
125/// the call with the kRowTag.
126///
127/// @code
128/// auto string_set = generic_result.AsSetOf<std::string>();
129/// std::string s = string_set[0];
130///
131/// auto string_vec = generic_result.AsContainer<std::vector<std::string>>();
132///
133/// // Extract first column into the composite type
134/// auto foo_set = generic_result.AsSetOf<FooBar>();
135/// auto foo_vec = generic_result.AsContainer<std::vector<FooBar>>();
136///
137/// // Extract the whole row, disambiguation
138/// auto foo_set = generic_result.AsSetOf<FooBar>(kRowTag);
139///
140/// @endcode
141///
142///
143/// ----------
144///
145/// @htmlonly <div class="bottom-nav"> @endhtmlonly
146/// ⇦ @ref pg_types | @ref pg_errors ⇨
147/// @htmlonly </div> @endhtmlonly
148
149template <typename T, typename ExtractionTag>
151 public:
154 static constexpr size_type npos = ResultSet::npos;
155 static constexpr ExtractionTag kExtractTag{};
156
157 //@{
158 /** @name Row container concept */
159 using const_iterator =
165
166 using value_type = T;
167 using pointer = const_iterator;
168
169// Forbidding assignments to operator[] result in debug, getting max
170// performance in release.
171#ifdef NDEBUG
172 using reference = value_type;
173#else
175#endif
176
177 //@}
179
180 /// Number of rows in the result set
181 size_type Size() const { return result_.Size(); }
182 bool IsEmpty() const { return Size() == 0; }
183 //@{
184 /** @name Container interface */
185 //@{
186 /** @name Row container interface */
187 //@{
188 /** @name Forward iteration */
189 const_iterator cbegin() const& { return const_iterator{result_.pimpl_, 0}; }
190 const_iterator begin() const& { return cbegin(); }
191 const_iterator cend() const& {
193 }
194 const_iterator end() const& { return cend(); }
195 const_iterator cbegin() const&& { ReportMisuse(); }
196 const_iterator begin() const&& { ReportMisuse(); }
197 const_iterator cend() const&& { ReportMisuse(); }
198 const_iterator end() const&& { ReportMisuse(); }
199 //@}
200 //@{
201 /** @name Reverse iteration */
204 }
205 const_reverse_iterator rbegin() const& { return crbegin(); }
208 }
209 const_reverse_iterator rend() const& { return crend(); }
214 //@}
215 /// @brief Access a row by index
216 /// @throws RowIndexOutOfBounds if index is out of bounds
217 // NOLINTNEXTLINE(readability-const-return-type)
219 return result_[index].template As<value_type>(kExtractTag);
220 }
221 // NOLINTNEXTLINE(readability-const-return-type)
222 reference operator[](size_type) const&& { ReportMisuse(); }
223 //@}
224 private:
225 [[noreturn]] static void ReportMisuse() {
226 static_assert(!sizeof(T), "keep the TypedResultSet before using, please");
227 }
228
230};
231
232} // namespace storages::postgres
233
234USERVER_NAMESPACE_END