userver: userver/storages/postgres/result_set.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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 <initializer_list>
7#include <limits>
8#include <memory>
9#include <optional>
10#include <tuple>
11#include <type_traits>
12#include <utility>
13#include <variant>
14
15#include <fmt/format.h>
16
17#include <userver/storages/postgres/exceptions.hpp>
18#include <userver/storages/postgres/io/supported_types.hpp>
19#include <userver/storages/postgres/postgres_fwd.hpp>
20
21#include <userver/storages/postgres/detail/const_data_iterator.hpp>
22
23#include <userver/compiler/demangle.hpp>
24#include <userver/logging/log.hpp>
25
26USERVER_NAMESPACE_BEGIN
27
28namespace storages::postgres {
29
30/// @page pg_process_results uPg: Working with result sets
31///
32/// A result set returned from Execute function is a thin read only wrapper
33/// around the libpq result. It can be copied around as it contains only a
34/// smart pointer to the underlying result set.
35///
36/// The result set's lifetime is not limited by the transaction in which it was
37/// created. In can be used after the transaction is committed or rolled back.
38///
39/// @par Iterating result set's rows
40///
41/// The ResultSet provides interface for range-based iteration over its rows.
42/// @code
43/// auto result = trx.Execute("select foo, bar from foobar");
44/// for (auto row : result) {
45/// // Process row data here
46/// }
47/// @endcode
48///
49/// Also rows can be accessed via indexing operators.
50/// @code
51/// auto result = trx.Execute("select foo, bar from foobar");
52/// for (auto idx = 0; idx < result.Size(); ++idx) {
53/// auto row = result[idx];
54/// // process row data here
55/// }
56/// @endcode
57///
58/// @par Accessing fields in a row
59///
60/// Fields in a row can be accessed by their index, by field name and can be
61/// iterated over. Invalid index or name will throw an exception.
62/// @code
63/// auto f1 = row[0];
64/// auto f2 = row["foo"];
65/// auto f3 = row[1];
66/// auto f4 = row["bar"];
67///
68/// for (auto f : row) {
69/// // Process field here
70/// }
71/// @endcode
72///
73/// @par Extracting field's data to variables
74///
75/// A Field object provides an interface to convert underlying buffer to a
76/// C++ variable of supported type. Please see @ref pg_types for more
77/// information on supported types.
78///
79/// Functions Field::As and Field::To can throw an exception if the field
80/// value is `null`. Their Field::Coalesce counterparts instead set the result
81/// to default value.
82///
83/// All data extraction functions can throw parsing errors (descendants of
84/// ResultSetError).
85///
86/// @code
87/// auto foo = row["foo"].As<int>();
88/// auto bar = row["bar"].As<std::string>();
89///
90/// foo = row["foo"].Coalesce(42);
91/// // There is no parser for char*, so a string object must be passed here.
92/// bar = row["bar"].Coalesce(std::string{"bar"});
93///
94/// row["foo"].To(foo);
95/// row["bar"].To(bar);
96///
97/// row["foo"].Coalesce(foo, 42);
98/// // The type is deduced by the first argument, so the second will be also
99/// // treated as std::string
100/// row["bar"].Coalesce(bar, "baz");
101/// @endcode
102///
103/// @par Extracting data directly from a Row object
104///
105/// Data can be extracted straight from a Row object to a pack or a tuple of
106/// user variables. The number of user variables cannot exceed the number of
107/// fields in the result. If it does, an exception will be thrown.
108///
109/// When used without additional parameters, the field values are extracted
110/// in the order of their appearance.
111///
112/// When a subset of the fields is needed, the fields can be specified by their
113/// indexes or names.
114///
115/// Row's data extraction functions throw exceptions as the field extraction
116/// functions. Also a FieldIndexOutOfBounds or FieldNameDoesntExist can be
117/// thrown.
118///
119/// Statements that return user-defined PostgreSQL type may be called as
120/// returning either one-column row with the whole type in it or as multi-column
121/// row with every column representing a field in the type. For the purpose of
122/// disambiguation, kRowTag may be used.
123///
124/// When a first column is extracted, it is expected that the result set
125/// contains the only column, otherwise an exception will be thrown.
126///
127/// @code
128/// auto [foo, bar] = row.As<int, std::string>();
129/// row.To(foo, bar);
130///
131/// auto [bar, foo] = row.As<std::string, int>({1, 0});
132/// row.To({1, 0}, bar, foo);
133///
134/// auto [bar, foo] = row.As<std::string, int>({"bar", "foo"});
135/// row.To({"bar", "foo"}, bar, foo);
136///
137/// // extract the whole row into a row-type structure.
138/// // The FooBar type must not have the C++ to PostgreSQL mapping in this case
139/// auto foobar = row.As<FooBar>();
140/// row.To(foobar);
141/// // If the FooBar type does have the mapping, the function call must be
142/// // disambiguated.
143/// foobar = row.As<FooBar>(kRowTag);
144/// row.To(foobar, kRowTag);
145/// @endcode
146///
147/// In the following example it is assumed that the row has a single column
148/// and the FooBar type is mapped to a PostgreSQL type.
149///
150/// @note The row is used to extract different types, it doesn't mean it will
151/// actually work with incompatible types.
152///
153/// @code
154/// auto foobar = row.As<FooBar>();
155/// row.To(foobar);
156///
157/// auto str = row.As<std::string>();
158/// auto i = row.As<int>();
159/// @endcode
160///
161///
162/// @par Converting a Row to a user row type
163///
164/// A row can be converted to a user type (tuple, structure, class), for more
165/// information on data type requirements see @ref pg_user_row_types
166///
167/// @todo Interface for converting rows to arbitrary user types
168///
169/// @par Converting ResultSet to a result set with user row types
170///
171/// A result set can be represented as a set of user row types or extracted to
172/// a container. For more information see @ref pg_user_row_types
173///
174/// @todo Interface for copying a ResultSet to an output iterator.
175///
176/// @par Non-select query results
177///
178/// @todo Process non-select result and provide interface. Do the docs.
179///
180///
181/// ----------
182///
183/// @htmlonly <div class="bottom-nav"> @endhtmlonly
184/// ⇦ @ref pg_run_queries | @ref pg_types ⇨
185/// @htmlonly </div> @endhtmlonly
186
188 /// Index of the field in the result set
189 std::size_t index;
190 /// @brief The object ID of the field's data type.
192 /// @brief The field name.
193 // TODO string_view
194 std::string name;
195 /// @brief If the field can be identified as a column of a specific table,
196 /// the object ID of the table; otherwise zero.
198 /// @brief If the field can be identified as a column of a specific table,
199 /// the attribute number of the column; otherwise zero.
201 /// @brief The data type size (see pg_type.typlen). Note that negative
202 /// values denote variable-width types.
203 Integer type_size;
204 /// @brief The type modifier (see pg_attribute.atttypmod). The meaning of
205 /// the modifier is type-specific.
207};
208
209/// @brief A wrapper for PGresult to access field descriptions.
211 public:
212 RowDescription(detail::ResultWrapperPtr res) : res_{std::move(res)} {}
213
214 /// Check that all fields can be read in binary format
215 /// @throw NoBinaryParser if any of the fields doesn't have a binary parser
216 void CheckBinaryFormat(const UserTypes& types) const;
217 // TODO interface for iterating field descriptions
218 private:
219 detail::ResultWrapperPtr res_;
220};
221
222class Row;
223class ResultSet;
224template <typename T, typename ExtractionTag>
225class TypedResultSet;
226
227/// @brief Accessor to a single field in a result set's row
228class Field {
229 public:
230 using size_type = std::size_t;
231
232 size_type RowIndex() const { return row_index_; }
233 size_type FieldIndex() const { return field_index_; }
234
235 //@{
236 /** @name Field metadata */
237 /// Field name as named in query
239 FieldDescription Description() const;
240
241 Oid GetTypeOid() const;
242 //@}
243
244 //@{
245 /** @name Data access */
246 bool IsNull() const;
247
248 /// Read the field's buffer into user-provided variable.
249 /// @throws FieldValueIsNull If the field is null and the C++ type is
250 /// not nullable.
251 template <typename T>
252 size_type To(T&& val) const {
253 using ValueType = typename std::decay<T>::type;
254 auto fb = GetBuffer();
255 return ReadNullable(fb, std::forward<T>(val),
256 io::traits::IsNullable<ValueType>{});
257 }
258
259 /// Read the field's buffer into user-provided variable.
260 /// If the field is null, set the variable to the default value.
261 template <typename T>
262 void Coalesce(T& val, const T& default_val) const {
263 if (!IsNull())
264 To(val);
265 else
266 val = default_val;
267 }
268
269 /// Convert the field's buffer into a C++ type.
270 /// @throws FieldValueIsNull If the field is null and the C++ type is
271 /// not nullable.
272 template <typename T>
273 typename std::decay<T>::type As() const {
274 T val{};
275 To(val);
276 return val;
277 }
278
279 /// Convert the field's buffer into a C++ type.
280 /// If the field is null, return default value.
281 template <typename T>
282 typename std::decay<T>::type Coalesce(const T& default_val) const {
283 if (IsNull()) return default_val;
284 return As<T>();
285 }
286 //@}
287 const io::TypeBufferCategory& GetTypeBufferCategories() const;
288
289 private:
290 io::FieldBuffer GetBuffer() const;
291
292 protected:
293 friend class Row;
294
295 Field(detail::ResultWrapperPtr res, size_type row, size_type col)
296 : res_{std::move(res)}, row_index_{row}, field_index_{col} {}
297
298 template <typename T>
299 size_type ReadNullable(const io::FieldBuffer& fb, T&& val,
300 std::true_type) const {
301 using ValueType = typename std::decay<T>::type;
302 using NullSetter = io::traits::GetSetNull<ValueType>;
303 if (fb.is_null) {
304 NullSetter::SetNull(val);
305 } else {
306 Read(fb, std::forward<T>(val));
307 }
308 return fb.length;
309 }
310 template <typename T>
311 size_type ReadNullable(const io::FieldBuffer& buffer, T&& val,
312 std::false_type) const {
313 if (buffer.is_null) {
314 throw FieldValueIsNull{field_index_, Name(), val};
315 } else {
316 Read(buffer, std::forward<T>(val));
317 }
318 return buffer.length;
319 }
320
321 //@{
322 /** @name Iteration support */
323 bool IsValid() const;
324 int Compare(const Field& rhs) const;
325 std::ptrdiff_t Distance(const Field& rhs) const;
326 Field& Advance(std::ptrdiff_t);
327 //@}
328
329 private:
330 template <typename T>
331 void Read(const io::FieldBuffer& buffer, T&& val) const {
332 using ValueType = typename std::decay<T>::type;
333 io::traits::CheckParser<ValueType>();
334 try {
335 io::ReadBuffer(buffer, std::forward<T>(val), GetTypeBufferCategories());
336 } catch (ResultSetError& ex) {
337 ex.AddMsgSuffix(fmt::format(
338 " (field #{} name `{}` C++ type `{}`. Postgres ResultSet error)",
339 field_index_, Name(), compiler::GetTypeName<T>()));
340 throw;
341 }
342 }
343
344 detail::ResultWrapperPtr res_;
345 size_type row_index_;
346 size_type field_index_;
347};
348
349/// @brief Iterator over fields in a result set's row
353 friend class Row;
354
355 ConstFieldIterator(detail::ResultWrapperPtr res, size_type row, size_type col)
356 : ConstDataIterator(std::move(res), row, col) {}
357};
358
359/// @brief Reverse iterator over fields in a result set's row
363 friend class Row;
364
365 ReverseConstFieldIterator(detail::ResultWrapperPtr res, size_type row,
366 size_type col)
367 : ConstDataIterator(std::move(res), row, col) {}
368};
369
370/// Data row in a result set
371/// This class is a mere accessor to underlying result set data buffer,
372/// must not be used outside of result set life scope.
373///
374/// Mimics field container
375class Row {
376 public:
377 //@{
378 /** @name Field container concept */
379 using size_type = std::size_t;
380 using const_iterator = ConstFieldIterator;
381 using const_reverse_iterator = ReverseConstFieldIterator;
382
383 using value_type = Field;
384 using reference = Field;
385 using pointer = const_iterator;
386 //@}
387
388 size_type RowIndex() const { return row_index_; }
389
390 RowDescription GetDescription() const { return {res_}; }
391 //@{
392 /** @name Field container interface */
393 /// Number of fields
394 size_type Size() const;
395
396 //@{
397 /** @name Forward iteration */
398 const_iterator cbegin() const;
399 const_iterator begin() const { return cbegin(); }
400 const_iterator cend() const;
401 const_iterator end() const { return cend(); }
402 //@}
403 //@{
404 /** @name Reverse iteration */
405 const_reverse_iterator crbegin() const;
406 const_reverse_iterator rbegin() const { return crbegin(); }
407 const_reverse_iterator crend() const;
408 const_reverse_iterator rend() const { return crend(); }
409 //@}
410
411 /// @brief Field access by index
412 /// @throws FieldIndexOutOfBounds if index is out of bounds
413 reference operator[](size_type index) const;
414 /// @brief Field access field by name
415 /// @throws FieldNameDoesntExist if the result set doesn't contain
416 /// such a field
417 reference operator[](const std::string& name) const;
418 //@}
419
420 //@{
421 /** @name Access to row's data */
422 /// Read the contents of the row to a user's row type or read the first
423 /// column into the value.
424 ///
425 /// If the user tries to read the first column into a variable, it must be the
426 /// only column in the result set. If the result set contains more than one
427 /// column, the function will throw NonSingleColumnResultSet. If the result
428 /// set is OK to contain more than one columns, the first column value should
429 /// be accessed via `row[0].To/As`.
430 ///
431 /// If the type is a 'row' type, the function will read the fields of the row
432 /// into the type's data members.
433 ///
434 /// If the type can be treated as both a row type and a composite type (the
435 /// type is mapped to a PostgreSQL type), the function will treat the type
436 /// as a type for the first (and the only) column.
437 ///
438 /// To read the all fields of the row as a row type, the To(T&&, RowTag)
439 /// should be used.
440 template <typename T>
441 void To(T&& val) const;
442
443 /// Function to disambiguate reading the row to a user's row type (values
444 /// of the row initialize user's type data members)
445 template <typename T>
446 void To(T&& val, RowTag) const;
447
448 /// Function to disambiguate reading the first column to a user's composite
449 /// type (PostgreSQL composite type in the row initializes user's type).
450 /// The same as calling To(T&& val) for a T mapped to a PostgreSQL type.
451 ///
452 /// See @ref pg_composite_types
453 template <typename T>
454 void To(T&& val, FieldTag) const;
455
456 /// Read fields into variables in order of their appearance in the row
457 template <typename... T>
458 void To(T&&... val) const;
459
460 /// @brief Parse values from the row and return the result.
461 ///
462 /// If there are more than one type arguments to the function, it will
463 /// return a tuple of those types.
464 ///
465 /// If there is a single type argument to the function, it will read the first
466 /// and the only column of the row or the whole row to the row type (depending
467 /// on C++ to PosgreSQL mapping presence) and return plain value of this type.
468 ///
469 /// @see To(T&&)
470 template <typename T, typename... Y>
471 auto As() const;
472
473 /// @brief Returns T initialized with values of the row.
474 /// @snippet storages/postgres/tests/typed_rows_pgtest.cpp RowTagSippet
475 ///
476 /// @see @ref pg_composite_types
477 template <typename T>
478 T As(RowTag) const {
479 T val{};
480 To(val, kRowTag);
481 return val;
482 }
483
484 /// @brief Returns T initialized with a single column value of the row.
485 /// @snippet storages/postgres/tests/composite_types_pgtest.cpp FieldTagSippet
486 ///
487 /// @see @ref pg_composite_types
488 template <typename T>
489 T As(FieldTag) const {
490 T val{};
491 To(val, kFieldTag);
492 return val;
493 }
494
495 /// Read fields into variables in order of their names in the first argument
496 template <typename... T>
497 void To(const std::initializer_list<std::string>& names, T&&... val) const;
498 template <typename... T>
499 std::tuple<T...> As(const std::initializer_list<std::string>& names) const;
500
501 /// Read fields into variables in order of their indexes in the first
502 /// argument
503 template <typename... T>
504 void To(const std::initializer_list<size_type>& indexes, T&&... val) const;
505 template <typename... T>
506 std::tuple<T...> As(const std::initializer_list<size_type>& indexes) const;
507 //@}
508
509 size_type IndexOfName(const std::string&) const;
510
511 protected:
512 friend class ResultSet;
513
514 Row(detail::ResultWrapperPtr res, size_type row)
515 : res_{std::move(res)}, row_index_{row} {}
516
517 //@{
518 /** @name Iteration support */
519 bool IsValid() const;
520 int Compare(const Row& rhs) const;
521 std::ptrdiff_t Distance(const Row& rhs) const;
522 Row& Advance(std::ptrdiff_t);
523 //@}
524 private:
525 detail::ResultWrapperPtr res_;
526 size_type row_index_;
527};
528
529/// @name Iterator over rows in a result set
533 friend class ResultSet;
534
535 ConstRowIterator(detail::ResultWrapperPtr res, size_type row)
536 : ConstDataIterator(std::move(res), row) {}
537};
538
539/// @name Reverse iterator over rows in a result set
543 friend class ResultSet;
544
545 ReverseConstRowIterator(detail::ResultWrapperPtr res, size_type row)
546 : ConstDataIterator(std::move(res), row) {}
547};
548
549/// @brief PostgreSQL result set
550///
551/// Provides random access to rows via indexing operations
552/// and bidirectional iteration via iterators.
553///
554/// ## Usage synopsis
555/// ```
556/// auto trx = ...;
557/// auto res = trx.Execute("select a, b from table");
558/// for (auto row : res) {
559/// // Process row data
560/// }
561/// ```
563 public:
564 using size_type = std::size_t;
565 using difference_type = std::ptrdiff_t;
566 static constexpr size_type npos = std::numeric_limits<size_type>::max();
567
568 //@{
569 /** @name Row container concept */
570 using const_iterator = ConstRowIterator;
571 using const_reverse_iterator = ReverseConstRowIterator;
572
573 using value_type = Row;
574 using reference = value_type;
575 using pointer = const_iterator;
576 //@}
577
578 explicit ResultSet(std::shared_ptr<detail::ResultWrapper> pimpl)
579 : pimpl_{std::move(pimpl)} {}
580
581 /// Number of rows in the result set
582 size_type Size() const;
583 bool IsEmpty() const { return Size() == 0; }
584
585 size_type RowsAffected() const;
586 std::string CommandStatus() const;
587
588 //@{
589 /** @name Row container interface */
590 //@{
591 /** @name Forward iteration */
592 const_iterator cbegin() const&;
593 const_iterator begin() const& { return cbegin(); }
594 const_iterator cend() const&;
595 const_iterator end() const& { return cend(); }
596
597 // One should store ResultSet before using its accessors
598 const_iterator cbegin() const&& = delete;
599 const_iterator begin() const&& = delete;
600 const_iterator cend() const&& = delete;
601 const_iterator end() const&& = delete;
602 //@}
603 //@{
604 /** @name Reverse iteration */
605 const_reverse_iterator crbegin() const&;
606 const_reverse_iterator rbegin() const& { return crbegin(); }
607 const_reverse_iterator crend() const&;
608 const_reverse_iterator rend() const& { return crend(); }
609 // One should store ResultSet before using its accessors
610 const_reverse_iterator crbegin() const&& = delete;
611 const_reverse_iterator rbegin() const&& = delete;
612 const_reverse_iterator crend() const&& = delete;
613 const_reverse_iterator rend() const&& = delete;
614 //@}
615
616 reference Front() const&;
617 reference Back() const&;
618 // One should store ResultSet before using its accessors
619 reference Front() const&& = delete;
620 reference Back() const&& = delete;
621
622 /// @brief Access a row by index
623 /// @throws RowIndexOutOfBounds if index is out of bounds
624 reference operator[](size_type index) const&;
625 // One should store ResultSet before using its accessors
626 reference operator[](size_type index) const&& = delete;
627 //@}
628
629 //@{
630 /** @name ResultSet metadata access */
631 // TODO ResultSet metadata access interface
632 size_type FieldCount() const;
633 RowDescription GetRowDescription() const& { return {pimpl_}; }
634 // One should store ResultSet before using its accessors
635 RowDescription GetRowDescription() const&& = delete;
636 //@}
637
638 //@{
639 /** @name Typed results */
640 /// @brief Get a wrapper for iterating over a set of typed results.
641 /// For more information see @ref psql_typed_results
642 template <typename T>
643 auto AsSetOf() const;
644 template <typename T>
645 auto AsSetOf(RowTag) const;
646 template <typename T>
647 auto AsSetOf(FieldTag) const;
648
649 /// @brief Extract data into a container.
650 /// For more information see @ref psql_typed_results
651 template <typename Container>
652 Container AsContainer() const;
653 template <typename Container>
654 Container AsContainer(RowTag) const;
655
656 /// @brief Extract first row into user type.
657 /// A single row result set is expected, will throw an exception when result
658 /// set size != 1
659 template <typename T>
660 auto AsSingleRow() const;
661 template <typename T>
662 auto AsSingleRow(RowTag) const;
663 template <typename T>
664 auto AsSingleRow(FieldTag) const;
665 //@}
666 private:
667 friend class detail::ConnectionImpl;
668 void FillBufferCategories(const UserTypes& types);
669 void SetBufferCategoriesFrom(const ResultSet&);
670
671 template <typename T, typename Tag>
672 friend class TypedResultSet;
673 friend class ConnectionImpl;
674
675 std::shared_ptr<detail::ResultWrapper> pimpl_;
676};
677
678namespace detail {
679
680template <typename T>
681struct IsOptionalFromOptional : std::false_type {};
682
683template <typename T>
684struct IsOptionalFromOptional<std::optional<std::optional<T>>>
685 : std::true_type {};
686
687template <typename T>
688struct IsOneVariant : std::false_type {};
689
690template <typename T>
691struct IsOneVariant<std::variant<T>> : std::true_type {};
692
693template <typename... Args>
694constexpr void AssertSaneTypeToDeserialize() {
695 static_assert(
696 !(IsOptionalFromOptional<
697 std::remove_const_t<std::remove_reference_t<Args>>>::value ||
698 ...),
699 "Attempt to get an optional<optional<T>> was detected. Such "
700 "optional-from-optional types are very error prone, obfuscate code and "
701 "are ambiguous to deserialize. Change the type to just optional<T>");
702 static_assert(
703 !(IsOneVariant<
704 std::remove_const_t<std::remove_reference_t<Args>>>::value ||
705 ...),
706 "Attempt to get an variant<T> was detected. Such variant from one type "
707 "obfuscates code. Change the type to just T");
708}
709
710//@{
711/** @name Sequental field extraction */
712template <typename IndexTuple, typename... T>
713struct RowDataExtractorBase;
714
715template <std::size_t... Indexes, typename... T>
716struct RowDataExtractorBase<std::index_sequence<Indexes...>, T...> {
717 static void ExtractValues(const Row& row, T&&... val) {
718 static_assert(sizeof...(Indexes) == sizeof...(T));
719
720 // We do it this way instead of row[Indexes...] to avoid Row::operator[]
721 // overhead - it copies + destroys a shared_ptr to ResultSet
722 auto it = row.begin();
723 const auto perform = [&](auto&& arg) {
724 it->To(std::forward<decltype(arg)>(arg));
725 ++it;
726 };
727 (perform(std::forward<T>(val)), ...);
728 }
729 static void ExtractTuple(const Row& row, std::tuple<T...>& val) {
730 static_assert(sizeof...(Indexes) == sizeof...(T));
731
732 // We do it this way instead of row[Indexes...] to avoid Row::operator[]
733 // overhead - it copies + destroys a shared_ptr to ResultSet
734 auto it = row.begin();
735 const auto perform = [&](auto& arg) {
736 it->To(arg);
737 ++it;
738 };
739 (perform(std::get<Indexes>(val)), ...);
740 }
741 static void ExtractTuple(const Row& row, std::tuple<T...>&& val) {
742 static_assert(sizeof...(Indexes) == sizeof...(T));
743
744 // We do it this way instead of row[Indexes...] to avoid Row::operator[]
745 // overhead - it copies + destroys a shared_ptr to ResultSet
746 auto it = row.begin();
747 const auto perform = [&](auto& arg) {
748 it->To(arg);
749 ++it;
750 };
751 (perform(std::get<Indexes>(val)), ...);
752 }
753
754 static void ExtractValues(const Row& row,
755 const std::initializer_list<std::string>& names,
756 T&&... val) {
757 (row[*(names.begin() + Indexes)].To(std::forward<T>(val)), ...);
758 }
759 static void ExtractTuple(const Row& row,
760 const std::initializer_list<std::string>& names,
761 std::tuple<T...>& val) {
762 std::tuple<T...> tmp{row[*(names.begin() + Indexes)].template As<T>()...};
763 tmp.swap(val);
764 }
765
766 static void ExtractValues(const Row& row,
767 const std::initializer_list<std::size_t>& indexes,
768 T&&... val) {
769 (row[*(indexes.begin() + Indexes)].To(std::forward<T>(val)), ...);
770 }
771 static void ExtractTuple(const Row& row,
772 const std::initializer_list<std::size_t>& indexes,
773 std::tuple<T...>& val) {
774 std::tuple<T...> tmp{row[*(indexes.begin() + Indexes)].template As<T>()...};
775 tmp.swap(val);
776 }
777};
778
779template <typename... T>
780struct RowDataExtractor
781 : RowDataExtractorBase<std::index_sequence_for<T...>, T...> {};
782
783template <typename T>
784struct TupleDataExtractor;
785template <typename... T>
786struct TupleDataExtractor<std::tuple<T...>>
787 : RowDataExtractorBase<std::index_sequence_for<T...>, T...> {};
788//@}
789
790} // namespace detail
791
792template <typename T>
793void Row::To(T&& val) const {
794 To(std::forward<T>(val), kFieldTag);
795}
796
797template <typename T>
798void Row::To(T&& val, RowTag) const {
799 detail::AssertSaneTypeToDeserialize<T>();
800 // Convert the val into a writable tuple and extract the data
801 using ValueType = std::decay_t<T>;
802 static_assert(io::traits::kIsRowType<ValueType>,
803 "This type cannot be used as a row type");
804 using RowType = io::RowType<ValueType>;
805 using TupleType = typename RowType::TupleType;
806 constexpr auto tuple_size = RowType::size;
807 if (tuple_size > Size()) {
808 throw InvalidTupleSizeRequested(Size(), tuple_size);
809 } else if (tuple_size < Size()) {
811 << "Row size is greater that the number of data members in "
812 "C++ user datatype "
813 << compiler::GetTypeName<T>();
814 }
815
816 detail::TupleDataExtractor<TupleType>::ExtractTuple(
817 *this, RowType::GetTuple(std::forward<T>(val)));
818}
819
820template <typename T>
821void Row::To(T&& val, FieldTag) const {
822 detail::AssertSaneTypeToDeserialize<T>();
823 using ValueType = std::decay_t<T>;
824 // composite types can be parsed without an explicit mapping
825 static_assert(io::traits::kIsMappedToPg<ValueType> ||
826 io::traits::kIsCompositeType<ValueType>,
827 "This type is not mapped to a PostgreSQL type");
828 // Read the first field into the type
829 if (Size() < 1) {
831 }
832 if (Size() > 1) {
833 throw NonSingleColumnResultSet{Size(), compiler::GetTypeName<T>(), "As"};
834 }
835 (*this)[0].To(std::forward<T>(val));
836}
837
838template <typename... T>
839void Row::To(T&&... val) const {
840 detail::AssertSaneTypeToDeserialize<T...>();
841 if (sizeof...(T) > Size()) {
842 throw InvalidTupleSizeRequested(Size(), sizeof...(T));
843 }
844 detail::RowDataExtractor<T...>::ExtractValues(*this, std::forward<T>(val)...);
845}
846
847template <typename T, typename... Y>
848auto Row::As() const {
849 if constexpr (sizeof...(Y) > 0) {
850 std::tuple<T, Y...> res;
851 To(res, kRowTag);
852 return res;
853 } else {
854 return As<T>(kFieldTag);
855 }
856}
857
858template <typename... T>
859void Row::To(const std::initializer_list<std::string>& names,
860 T&&... val) const {
861 detail::AssertSaneTypeToDeserialize<T...>();
862 if (sizeof...(T) != names.size()) {
863 throw FieldTupleMismatch(names.size(), sizeof...(T));
864 }
865 detail::RowDataExtractor<T...>::ExtractValues(*this, names,
866 std::forward<T>(val)...);
867}
868
869template <typename... T>
870std::tuple<T...> Row::As(
871 const std::initializer_list<std::string>& names) const {
872 if (sizeof...(T) != names.size()) {
873 throw FieldTupleMismatch(names.size(), sizeof...(T));
874 }
875 std::tuple<T...> res;
876 detail::RowDataExtractor<T...>::ExtractTuple(*this, names, res);
877 return res;
878}
879
880template <typename... T>
881void Row::To(const std::initializer_list<size_type>& indexes,
882 T&&... val) const {
883 detail::AssertSaneTypeToDeserialize<T...>();
884 if (sizeof...(T) != indexes.size()) {
885 throw FieldTupleMismatch(indexes.size(), sizeof...(T));
886 }
887 detail::RowDataExtractor<T...>::ExtractValues(*this, indexes,
888 std::forward<T>(val)...);
889}
890
891template <typename... T>
892std::tuple<T...> Row::As(
893 const std::initializer_list<size_type>& indexes) const {
894 if (sizeof...(T) != indexes.size()) {
895 throw FieldTupleMismatch(indexes.size(), sizeof...(T));
896 }
897 std::tuple<T...> res;
898 detail::RowDataExtractor<T...>::ExtractTuple(*this, indexes, res);
899 return res;
900}
901
902template <typename T>
903auto ResultSet::AsSetOf() const {
904 return AsSetOf<T>(kFieldTag);
905}
906
907template <typename T>
908auto ResultSet::AsSetOf(RowTag) const {
909 detail::AssertSaneTypeToDeserialize<T>();
910 using ValueType = std::decay_t<T>;
911 static_assert(io::traits::kIsRowType<ValueType>,
912 "This type cannot be used as a row type");
913 return TypedResultSet<T, RowTag>{*this};
914}
915
916template <typename T>
917auto ResultSet::AsSetOf(FieldTag) const {
918 detail::AssertSaneTypeToDeserialize<T>();
919 using ValueType = std::decay_t<T>;
920 // composite types can be parsed without an explicit mapping
921 static_assert(io::traits::kIsMappedToPg<ValueType> ||
922 io::traits::kIsCompositeType<ValueType>,
923 "This type is not mapped to a PostgreSQL type");
924 if (FieldCount() > 1) {
925 throw NonSingleColumnResultSet{FieldCount(), compiler::GetTypeName<T>(),
926 "AsSetOf"};
927 }
928 return TypedResultSet<T, FieldTag>{*this};
929}
930
931template <typename Container>
932Container ResultSet::AsContainer() const {
933 detail::AssertSaneTypeToDeserialize<Container>();
934 using ValueType = typename Container::value_type;
935 Container c;
936 if constexpr (io::traits::kCanReserve<Container>) {
937 c.reserve(Size());
938 }
939 auto res = AsSetOf<ValueType>();
940 std::copy(res.begin(), res.end(), io::traits::Inserter(c));
941 return c;
942}
943
944template <typename Container>
945Container ResultSet::AsContainer(RowTag) const {
946 detail::AssertSaneTypeToDeserialize<Container>();
947 using ValueType = typename Container::value_type;
948 Container c;
949 if constexpr (io::traits::kCanReserve<Container>) {
950 c.reserve(Size());
951 }
952 auto res = AsSetOf<ValueType>(kRowTag);
953 std::copy(res.begin(), res.end(), io::traits::Inserter(c));
954 return c;
955}
956
957template <typename T>
958auto ResultSet::AsSingleRow() const {
959 return AsSingleRow<T>(kFieldTag);
960}
961
962template <typename T>
963auto ResultSet::AsSingleRow(RowTag) const {
964 detail::AssertSaneTypeToDeserialize<T>();
965 if (Size() != 1) {
967 }
968 return Front().As<T>(kRowTag);
969}
970
971template <typename T>
972auto ResultSet::AsSingleRow(FieldTag) const {
973 detail::AssertSaneTypeToDeserialize<T>();
974 if (Size() != 1) {
976 }
977 return Front().As<T>(kFieldTag);
978}
979
980} // namespace storages::postgres
981
982USERVER_NAMESPACE_END
983
984#include <userver/storages/postgres/typed_result_set.hpp>