userver: userver/storages/postgres/result_set.hpp Source File
Loading...
Searching...
No Matches
result_set.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/postgres/result_set.hpp
4/// @brief Result accessors
5
6#include <limits>
7#include <memory>
8#include <optional>
9#include <type_traits>
10#include <utility>
11
12#include <fmt/format.h>
13
14#include <userver/storages/postgres/detail/typed_rows.hpp>
15#include <userver/storages/postgres/exceptions.hpp>
16#include <userver/storages/postgres/io/supported_types.hpp>
17#include <userver/storages/postgres/row.hpp>
18
19#include <userver/compiler/demangle.hpp>
20#include <userver/logging/log.hpp>
21
22USERVER_NAMESPACE_BEGIN
23
24namespace storages::postgres {
25
26template <typename T, typename ExtractionTag>
27class TypedResultSet;
28
29/// @brief PostgreSQL result set
30///
31/// Provides random access to rows via indexing operations
32/// and bidirectional iteration via iterators.
33///
34/// ## Usage synopsis
35/// ```
36/// auto trx = ...;
37/// auto res = trx.Execute("select a, b from table");
38/// for (auto row : res) {
39/// // Process row data
40/// }
41/// ```
42class ResultSet {
43public:
44 using size_type = std::size_t;
45 using difference_type = std::ptrdiff_t;
46 static constexpr size_type npos = std::numeric_limits<size_type>::max();
47
48 //@{
49 /** @name Row container concept */
50 using const_iterator = ConstRowIterator;
51 using const_reverse_iterator = ReverseConstRowIterator;
52
53 using value_type = Row;
54 using reference = value_type;
55 using pointer = const_iterator;
56 //@}
57
58 explicit ResultSet(std::shared_ptr<detail::ResultWrapper> pimpl)
59 : pimpl_{std::move(pimpl)}
60 {}
61
62 /// Number of rows in the result set
63 size_type Size() const;
64 bool IsEmpty() const { return Size() == 0; }
65
66 size_type RowsAffected() const;
67 std::string CommandStatus() const;
68
69 //@{
70 /** @name Row container interface */
71 //@{
72 /** @name Forward iteration */
73 const_iterator cbegin() const&;
74 const_iterator begin() const& { return cbegin(); }
75 const_iterator cend() const&;
76 const_iterator end() const& { return cend(); }
77
78 // One should store ResultSet before using its accessors
79 const_iterator cbegin() const&& = delete;
80 const_iterator begin() const&& = delete;
81 const_iterator cend() const&& = delete;
82 const_iterator end() const&& = delete;
83 //@}
84 //@{
85 /** @name Reverse iteration */
86 const_reverse_iterator crbegin() const&;
87 const_reverse_iterator rbegin() const& { return crbegin(); }
88 const_reverse_iterator crend() const&;
89 const_reverse_iterator rend() const& { return crend(); }
90 // One should store ResultSet before using its accessors
91 const_reverse_iterator crbegin() const&& = delete;
92 const_reverse_iterator rbegin() const&& = delete;
93 const_reverse_iterator crend() const&& = delete;
94 const_reverse_iterator rend() const&& = delete;
95 //@}
96
97 reference Front() const&;
98 reference Back() const&;
99 // One should store ResultSet before using its accessors
100 reference Front() const&& = delete;
101 reference Back() const&& = delete;
102
103 /// @brief Access a row by index
104 /// @throws RowIndexOutOfBounds if index is out of bounds
105 reference operator[](size_type index) const&;
106 // One should store ResultSet before using its accessors
107 reference operator[](size_type index) const&& = delete;
108 //@}
109
110 //@{
111 /** @name ResultSet metadata access */
112 // TODO ResultSet metadata access interface
113 size_type FieldCount() const;
114 RowDescription GetRowDescription() const& { return {pimpl_}; }
115 // One should store ResultSet before using its accessors
116 RowDescription GetRowDescription() const&& = delete;
117 //@}
118
119 //@{
120 /** @name Typed results */
121 /// @brief Get a wrapper for iterating over a set of typed results.
122 /// For more information see @ref scripts/docs/en/userver/pg/user_row_types.md
123 template <typename T>
124 auto AsSetOf() const;
125 template <typename T>
126 auto AsSetOf(RowTag) const;
127 template <typename T>
128 auto AsSetOf(FieldTag) const;
129
130 /// @brief Extract data into a container.
131 /// For more information see @ref scripts/docs/en/userver/pg/user_row_types.md
132 template <typename Container>
133 Container AsContainer() const;
134 template <typename Container>
135 Container AsContainer(RowTag) const;
136
137 /// @brief Extract first row into user type.
138 /// A single row result set is expected, will throw an exception when result
139 /// set size != 1
140 template <typename T>
141 auto AsSingleRow() const;
142 template <typename T>
143 auto AsSingleRow(RowTag) const;
144 template <typename T>
145 auto AsSingleRow(FieldTag) const;
146
147 /// @brief Extract first row into user type.
148 /// @returns A single row result set if non empty result was returned, empty
149 /// std::optional otherwise
150 /// @throws exception when result set size > 1
151 template <typename T>
152 std::optional<T> AsOptionalSingleRow() const;
153 template <typename T>
154 std::optional<T> AsOptionalSingleRow(RowTag) const;
155 template <typename T>
156 std::optional<T> AsOptionalSingleRow(FieldTag) const;
157 //@}
158private:
159 friend class detail::ConnectionImpl;
160 void FillBufferCategories(const UserTypes& types);
161 void SetBufferCategoriesFrom(const ResultSet&);
162
163 template <typename T, typename Tag>
164 friend class TypedResultSet;
165 friend class ConnectionImpl;
166
167 std::shared_ptr<detail::ResultWrapper> pimpl_;
168};
169
170template <typename T>
171auto ResultSet::AsSetOf() const {
172 return AsSetOf<T>(kFieldTag);
173}
174
175template <typename T>
176auto ResultSet::AsSetOf(RowTag) const {
177 detail::AssertSaneTypeToDeserialize<T>();
178 using ValueType = std::remove_cvref_t<T>;
179 io::traits::AssertIsValidRowType<ValueType>();
180 return TypedResultSet<T, RowTag>{*this};
181}
182
183template <typename T>
184auto ResultSet::AsSetOf(FieldTag) const {
185 detail::AssertSaneTypeToDeserialize<T>();
186 using ValueType = std::remove_cvref_t<T>;
187 detail::AssertRowTypeIsMappedToPgOrIsCompositeType<ValueType>();
188 if (FieldCount() > 1) {
189 throw NonSingleColumnResultSet{FieldCount(), compiler::GetTypeName<T>(), "AsSetOf"};
190 }
191 return TypedResultSet<T, FieldTag>{*this};
192}
193
194template <typename Container>
195Container ResultSet::AsContainer() const {
196 detail::AssertSaneTypeToDeserialize<Container>();
197 using ValueType = typename Container::value_type;
198 Container c;
199 if constexpr (io::traits::CanReserve<Container>) {
200 c.reserve(Size());
201 }
202 auto res = AsSetOf<ValueType>();
203
204 auto inserter = io::traits::Inserter(c);
205 auto row_it = res.begin();
206 for (std::size_t i = 0; i < res.Size(); ++i, ++row_it, ++inserter) {
207 *inserter = *row_it;
208 }
209
210 return c;
211}
212
213template <typename Container>
214Container ResultSet::AsContainer(RowTag) const {
215 detail::AssertSaneTypeToDeserialize<Container>();
216 using ValueType = typename Container::value_type;
217 Container c;
218 if constexpr (io::traits::CanReserve<Container>) {
219 c.reserve(Size());
220 }
221 auto res = AsSetOf<ValueType>(kRowTag);
222
223 auto inserter = io::traits::Inserter(c);
224 auto row_it = res.begin();
225 for (std::size_t i = 0; i < res.Size(); ++i, ++row_it, ++inserter) {
226 *inserter = *row_it;
227 }
228
229 return c;
230}
231
232template <typename T>
233auto ResultSet::AsSingleRow() const {
234 return AsSingleRow<T>(kFieldTag);
235}
236
237template <typename T>
238auto ResultSet::AsSingleRow(RowTag) const {
239 detail::AssertSaneTypeToDeserialize<T>();
240 if (Size() != 1) {
242 }
243 return Front().As<T>(kRowTag);
244}
245
246template <typename T>
247auto ResultSet::AsSingleRow(FieldTag) const {
248 detail::AssertSaneTypeToDeserialize<T>();
249 if (Size() != 1) {
251 }
252 return Front().As<T>(kFieldTag);
253}
254
255template <typename T>
256std::optional<T> ResultSet::AsOptionalSingleRow() const {
257 return AsOptionalSingleRow<T>(kFieldTag);
258}
259
260template <typename T>
261std::optional<T> ResultSet::AsOptionalSingleRow(RowTag) const {
262 return IsEmpty() ? std::nullopt : std::optional<T>{AsSingleRow<T>(kRowTag)};
263}
264
265template <typename T>
266std::optional<T> ResultSet::AsOptionalSingleRow(FieldTag) const {
267 return IsEmpty() ? std::nullopt : std::optional<T>{AsSingleRow<T>(kFieldTag)};
268}
269
270template <typename T, typename ExtractionTag>
271class TypedResultSet {
272public:
273 using size_type = ResultSet::size_type;
274 using difference_type = ResultSet::difference_type;
275 static constexpr size_type npos = ResultSet::npos;
276 static constexpr ExtractionTag kExtractTag{};
277
278 //@{
279 /** @name Row container concept */
280 using const_iterator = detail::ConstTypedRowIterator<T, ExtractionTag, detail::IteratorDirection::kForward>;
281 using const_reverse_iterator = detail::ConstTypedRowIterator<T, ExtractionTag, detail::IteratorDirection::kReverse>;
282
283 using value_type = T;
284 using pointer = const_iterator;
285
286// Forbidding assignments to operator[] result in debug, getting max
287// performance in release.
288#ifdef NDEBUG
289 using reference = value_type;
290#else
291 using reference = std::add_const_t<value_type>;
292#endif
293
294 //@}
295 explicit TypedResultSet(ResultSet result)
296 : result_{std::move(result)}
297 {}
298
299 /// Number of rows in the result set
300 size_type Size() const { return result_.Size(); }
301 bool IsEmpty() const { return Size() == 0; }
302 //@{
303 /** @name Container interface */
304 //@{
305 /** @name Row container interface */
306 //@{
307 /** @name Forward iteration */
308 const_iterator cbegin() const& { return const_iterator{result_.pimpl_, 0}; }
309 const_iterator begin() const& { return cbegin(); }
310 const_iterator cend() const& { return const_iterator{result_.pimpl_, Size()}; }
311 const_iterator end() const& { return cend(); }
312 const_iterator cbegin() const&& { ReportMisuse(); }
313 const_iterator begin() const&& { ReportMisuse(); }
314 const_iterator cend() const&& { ReportMisuse(); }
315 const_iterator end() const&& { ReportMisuse(); }
316 //@}
317 //@{
318 /** @name Reverse iteration */
319 const_reverse_iterator crbegin() const& { return const_reverse_iterator(result_.pimpl_, Size() - 1); }
320 const_reverse_iterator rbegin() const& { return crbegin(); }
321 const_reverse_iterator crend() const& { return const_reverse_iterator(result_.pimpl_, npos); }
322 const_reverse_iterator rend() const& { return crend(); }
323 const_reverse_iterator crbegin() const&& { ReportMisuse(); }
324 const_reverse_iterator rbegin() const&& { ReportMisuse(); }
325 const_reverse_iterator crend() const&& { ReportMisuse(); }
326 const_reverse_iterator rend() const&& { ReportMisuse(); }
327 //@}
328 /// @brief Access a row by index
329 /// @throws RowIndexOutOfBounds if index is out of bounds
330 // NOLINTNEXTLINE(readability-const-return-type)
331 reference operator[](size_type index) const& { return result_[index].template As<value_type>(kExtractTag); }
332 // NOLINTNEXTLINE(readability-const-return-type)
333 reference operator[](size_type) const&& { ReportMisuse(); }
334 //@}
335private:
336 [[noreturn]] static void ReportMisuse() {
337 static_assert(!sizeof(T), "keep the TypedResultSet before using, please");
338 }
339
340 ResultSet result_;
341};
342
343} // namespace storages::postgres
344
345USERVER_NAMESPACE_END