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 <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>