6#include <initializer_list>
15#include <fmt/format.h>
17#include <userver/storages/postgres/exceptions.hpp>
18#include <userver/storages/postgres/io/supported_types.hpp>
19#include <userver/storages/postgres/postgres_fwd.hpp>
21#include <userver/storages/postgres/detail/const_data_iterator.hpp>
23#include <userver/compiler/demangle.hpp>
24#include <userver/logging/log.hpp>
26USERVER_NAMESPACE_BEGIN
213 RowDescription(detail::ResultWrapperPtr res) : res_{std::move(res)} {}
221 detail::ResultWrapperPtr res_;
226template <
typename T,
typename ExtractionTag>
229class FieldView
final {
231 using size_type = std::size_t;
233 FieldView(
const detail::ResultWrapper& res, size_type row_index, size_type field_index)
234 : res_{res}, row_index_{row_index}, field_index_{field_index} {}
236 template <
typename T>
237 size_type To(T&& val)
const {
238 using ValueType =
typename std::decay<T>::type;
239 auto fb = GetBuffer();
240 return ReadNullable(fb, std::forward<T>(val), io::
traits::
IsNullable<ValueType>{});
245 std::string_view Name()
const;
246 Oid GetTypeOid()
const;
247 const io::TypeBufferCategory& GetTypeBufferCategories()
const;
249 template <
typename T>
250 size_type ReadNullable(
const io::
FieldBuffer& fb, T&& val, std::true_type)
const {
251 using ValueType =
typename std::decay<T>::type;
254 NullSetter::SetNull(val);
256 Read(fb, std::forward<T>(val));
261 template <
typename T>
262 size_type ReadNullable(
const io::
FieldBuffer& buffer, T&& val, std::false_type)
const {
263 if (buffer.is_null) {
264 throw FieldValueIsNull{field_index_, Name(), val};
266 Read(buffer, std::forward<T>(val));
268 return buffer.length;
271 template <
typename T>
272 void Read(
const io::
FieldBuffer& buffer, T&& val)
const {
273 using ValueType =
typename std::decay<T>::type;
274 io::
traits::CheckParser<ValueType>();
276 io::ReadBuffer(buffer, std::forward<T>(val), GetTypeBufferCategories());
279 ex.AddMsgPrefix(fmt::format(
280 "Error while reading field #{0} '{1}' which database type {2} as a C++ type '{3}'. Refer to "
281 "the 'Supported data types' in the documentation to make sure that the database type is actually "
282 "representable as a C++ type '{3}'. Error details: ",
285 impl::OidPrettyPrint(GetTypeOid()),
286 compiler::GetTypeName<T>()
291 ex.AddMsgSuffix(fmt::format(
" (ResultSet error while reading field #{} name `{}`)", field_index_, Name()));
296 const detail::ResultWrapper& res_;
297 const size_type row_index_;
298 const size_type field_index_;
304 using size_type = std::size_t;
306 size_type RowIndex()
const {
return row_index_; }
307 size_type FieldIndex()
const {
return field_index_; }
315 Oid GetTypeOid()
const;
325 template <
typename T>
326 size_type
To(T&& val)
const {
327 return FieldView{*res_, row_index_, field_index_}.To(std::forward<T>(val));
332 template <
typename T>
333 void Coalesce(T& val,
const T& default_val)
const {
343 template <
typename T>
344 typename std::decay<T>::
type As()
const {
352 template <
typename T>
354 if (IsNull())
return default_val;
358 const io::TypeBufferCategory& GetTypeBufferCategories()
const;
365 Field(detail::ResultWrapperPtr res, size_type row, size_type col)
366 : res_{std::move(res)}, row_index_{row}, field_index_{col} {}
370 bool IsValid()
const;
371 int Compare(
const Field& rhs)
const;
372 std::ptrdiff_t Distance(
const Field& rhs)
const;
373 Field& Advance(std::ptrdiff_t);
377 detail::ResultWrapperPtr res_;
378 size_type row_index_{0};
379 size_type field_index_{0};
386 ConstFieldIterator() =
default;
391 ConstFieldIterator(detail::ResultWrapperPtr res, size_type row, size_type col)
392 : ConstDataIterator(std::move(res), row, col) {}
399 ReverseConstFieldIterator() =
default;
404 ReverseConstFieldIterator(detail::ResultWrapperPtr res, size_type row, size_type col)
405 : ConstDataIterator(std::move(res), row, col) {}
417 using size_type = std::size_t;
421 using value_type =
Field;
422 using reference =
Field;
423 using pointer = const_iterator;
426 size_type RowIndex()
const {
return row_index_; }
436 const_iterator cbegin()
const;
437 const_iterator begin()
const {
return cbegin(); }
438 const_iterator cend()
const;
439 const_iterator end()
const {
return cend(); }
443 const_reverse_iterator crbegin()
const;
444 const_reverse_iterator rbegin()
const {
return crbegin(); }
445 const_reverse_iterator crend()
const;
446 const_reverse_iterator rend()
const {
return crend(); }
455 reference
operator[](
const std::string& name)
const;
478 template <
typename T>
479 void To(T&& val)
const;
483 template <
typename T>
489 template <
typename T>
493 template <
typename... T>
494 void To(T&&... val)
const;
506 template <
typename T,
typename... Y>
511 template <
typename T>
520 template <
typename T>
528 template <
typename... T>
529 void To(
const std::initializer_list<std::string>& names, T&&... val)
const;
530 template <
typename... T>
531 std::tuple<T...> As(
const std::initializer_list<std::string>& names)
const;
535 template <
typename... T>
536 void To(
const std::initializer_list<size_type>& indexes, T&&... val)
const;
537 template <
typename... T>
538 std::tuple<T...> As(
const std::initializer_list<size_type>& indexes)
const;
541 size_type IndexOfName(
const std::string&)
const;
543 FieldView GetFieldView(size_type index)
const;
550 Row(detail::ResultWrapperPtr res, size_type row) : res_{std::move(res)}, row_index_{row} {}
554 bool IsValid()
const;
555 int Compare(
const Row& rhs)
const;
556 std::ptrdiff_t Distance(
const Row& rhs)
const;
557 Row& Advance(std::ptrdiff_t);
560 detail::ResultWrapperPtr res_;
561 size_type row_index_{0};
567 ConstRowIterator() =
default;
572 ConstRowIterator(detail::ResultWrapperPtr res, size_type row) : ConstDataIterator(std::move(res), row) {}
579 ReverseConstRowIterator() =
default;
584 ReverseConstRowIterator(detail::ResultWrapperPtr res, size_type row) : ConstDataIterator(std::move(res), row) {}
602 using size_type = std::size_t;
603 using difference_type = std::ptrdiff_t;
604 static constexpr size_type npos = std::numeric_limits<size_type>::max();
611 using value_type =
Row;
612 using reference = value_type;
613 using pointer = const_iterator;
616 explicit ResultSet(std::shared_ptr<detail::ResultWrapper> pimpl) : pimpl_{std::move(pimpl)} {}
620 bool IsEmpty()
const {
return Size() == 0; }
622 size_type RowsAffected()
const;
623 std::string CommandStatus()
const;
629 const_iterator cbegin()
const&;
630 const_iterator begin()
const& {
return cbegin(); }
631 const_iterator cend()
const&;
632 const_iterator end()
const& {
return cend(); }
635 const_iterator cbegin()
const&& =
delete;
636 const_iterator begin()
const&& =
delete;
637 const_iterator cend()
const&& =
delete;
638 const_iterator end()
const&& =
delete;
642 const_reverse_iterator crbegin()
const&;
643 const_reverse_iterator rbegin()
const& {
return crbegin(); }
644 const_reverse_iterator crend()
const&;
645 const_reverse_iterator rend()
const& {
return crend(); }
647 const_reverse_iterator crbegin()
const&& =
delete;
648 const_reverse_iterator rbegin()
const&& =
delete;
649 const_reverse_iterator crend()
const&& =
delete;
650 const_reverse_iterator rend()
const&& =
delete;
653 reference Front()
const&;
654 reference Back()
const&;
656 reference Front()
const&& =
delete;
657 reference Back()
const&& =
delete;
663 reference operator[](size_type index)
const&& =
delete;
669 size_type FieldCount()
const;
679 template <
typename T>
681 template <
typename T>
682 auto AsSetOf(
RowTag)
const;
683 template <
typename T>
688 template <
typename Container>
690 template <
typename Container>
691 Container AsContainer(
RowTag)
const;
696 template <
typename T>
698 template <
typename T>
699 auto AsSingleRow(
RowTag)
const;
700 template <
typename T>
707 template <
typename T>
709 template <
typename T>
710 std::optional<T> AsOptionalSingleRow(
RowTag)
const;
711 template <
typename T>
712 std::optional<T> AsOptionalSingleRow(
FieldTag)
const;
715 friend class detail::ConnectionImpl;
716 void FillBufferCategories(
const UserTypes& types);
717 void SetBufferCategoriesFrom(
const ResultSet&);
719 template <
typename T,
typename Tag>
720 friend class TypedResultSet;
721 friend class ConnectionImpl;
723 std::shared_ptr<detail::ResultWrapper> pimpl_;
729struct IsOptionalFromOptional : std::false_type {};
732struct IsOptionalFromOptional<std::optional<std::optional<T>>> : std::true_type {};
735struct IsOneVariant : std::false_type {};
738struct IsOneVariant<std::variant<T>> : std::true_type {};
740template <
typename... Args>
741constexpr void AssertSaneTypeToDeserialize() {
743 !(IsOptionalFromOptional<std::remove_const_t<std::remove_reference_t<Args>>>::value || ...),
744 "Attempt to get an optional<optional<T>> was detected. Such "
745 "optional-from-optional types are very error prone, obfuscate code and "
746 "are ambiguous to deserialize. Change the type to just optional<T>"
749 !(IsOneVariant<std::remove_const_t<std::remove_reference_t<Args>>>::value || ...),
750 "Attempt to get an variant<T> was detected. Such variant from one type "
751 "obfuscates code. Change the type to just T"
757template <
typename IndexTuple,
typename... T>
758struct RowDataExtractorBase;
760template <std::size_t... Indexes,
typename... T>
761struct RowDataExtractorBase<std::index_sequence<Indexes...>, T...> {
762 static void ExtractValues(
const Row& row, T&&... val) {
763 static_assert(
sizeof...(Indexes) ==
sizeof...(T));
765 std::size_t field_index = 0;
766 const auto perform = [&](
auto&& arg) { row.GetFieldView(field_index++).To(std::forward<
decltype(arg)>(arg)); };
767 (perform(std::forward<T>(val)), ...);
769 static void ExtractTuple(
const Row& row, std::tuple<T...>& val) {
770 static_assert(
sizeof...(Indexes) ==
sizeof...(T));
772 std::size_t field_index = 0;
773 const auto perform = [&](
auto& arg) { row.GetFieldView(field_index++).To(arg); };
774 (perform(std::get<Indexes>(val)), ...);
776 static void ExtractTuple(
const Row& row, std::tuple<T...>&& val) {
777 static_assert(
sizeof...(Indexes) ==
sizeof...(T));
779 std::size_t field_index = 0;
780 const auto perform = [&](
auto& arg) { row.GetFieldView(field_index++).To(arg); };
781 (perform(std::get<Indexes>(val)), ...);
784 static void ExtractValues(
const Row& row,
const std::initializer_list<std::string>& names, T&&... val) {
785 (row[*(names.begin() + Indexes)].To(std::forward<T>(val)), ...);
787 static void ExtractTuple(
const Row& row,
const std::initializer_list<std::string>& names, std::tuple<T...>& val) {
788 std::tuple<T...> tmp{row[*(names.begin() + Indexes)].
template As<T>()...};
792 static void ExtractValues(
const Row& row,
const std::initializer_list<std::size_t>& indexes, T&&... val) {
793 (row[*(indexes.begin() + Indexes)].To(std::forward<T>(val)), ...);
795 static void ExtractTuple(
const Row& row,
const std::initializer_list<std::size_t>& indexes, std::tuple<T...>& val) {
796 std::tuple<T...> tmp{row[*(indexes.begin() + Indexes)].
template As<T>()...};
801template <
typename... T>
802struct RowDataExtractor : RowDataExtractorBase<std::index_sequence_for<T...>, T...> {};
805struct TupleDataExtractor;
806template <
typename... T>
807struct TupleDataExtractor<std::tuple<T...>> : RowDataExtractorBase<std::index_sequence_for<T...>, T...> {};
810template <
typename RowType>
811constexpr void AssertRowTypeIsMappedToPgOrIsCompositeType() {
814 io::
traits::kIsMappedToPg<RowType> || io::
traits::kIsCompositeType<RowType>,
815 "Row type must be mapped to pg type(CppToUserPg) or one of the "
817 "1. primitive type. "
819 "3. Aggregation type. See std::aggregation. "
820 "4. Has a Introspect method that makes the std::tuple from your "
822 "For more info see `uPg: Typed PostgreSQL results` chapter in docs."
830 To(std::forward<T>(val), kFieldTag);
835 detail::AssertSaneTypeToDeserialize<T>();
837 using ValueType = std::decay_t<T>;
838 io::traits::AssertIsValidRowType<ValueType>();
839 using RowType = io::RowType<ValueType>;
840 using TupleType =
typename RowType::TupleType;
841 constexpr auto tuple_size = RowType::size;
842 if (tuple_size > Size()) {
843 throw InvalidTupleSizeRequested(Size(), tuple_size);
844 }
else if (tuple_size < Size()) {
850 detail::TupleDataExtractor<TupleType>::ExtractTuple(*
this, RowType::GetTuple(std::forward<T>(val)));
855 detail::AssertSaneTypeToDeserialize<T>();
856 using ValueType = std::decay_t<T>;
857 detail::AssertRowTypeIsMappedToPgOrIsCompositeType<ValueType>();
865 (*
this)
[0
].To(std::forward<T>(val));
868template <
typename... T>
870 detail::AssertSaneTypeToDeserialize<T...>();
871 if (
sizeof...(T) >
Size()) {
874 detail::RowDataExtractor<T...>::ExtractValues(*
this, std::forward<T>(val)...);
877template <
typename T,
typename... Y>
879 if constexpr (
sizeof...(Y) > 0) {
880 std::tuple<T, Y...> res;
884 return As<T>(kFieldTag);
888template <
typename... T>
889void Row::
To(
const std::initializer_list<std::string>& names, T&&... val)
const {
890 detail::AssertSaneTypeToDeserialize<T...>();
891 if (
sizeof...(T) != names.size()) {
892 throw FieldTupleMismatch(names.size(),
sizeof...(T));
894 detail::RowDataExtractor<T...>::ExtractValues(*
this, names, std::forward<T>(val)...);
897template <
typename... T>
898std::tuple<T...>
Row::As(
const std::initializer_list<std::string>& names)
const {
899 if (
sizeof...(T) != names.size()) {
900 throw FieldTupleMismatch(names.size(),
sizeof...(T));
902 std::tuple<T...> res;
903 detail::RowDataExtractor<T...>::ExtractTuple(*
this, names, res);
907template <
typename... T>
908void Row::
To(
const std::initializer_list<size_type>& indexes, T&&... val)
const {
909 detail::AssertSaneTypeToDeserialize<T...>();
910 if (
sizeof...(T) != indexes.size()) {
911 throw FieldTupleMismatch(indexes.size(),
sizeof...(T));
913 detail::RowDataExtractor<T...>::ExtractValues(*
this, indexes, std::forward<T>(val)...);
916template <
typename... T>
917std::tuple<T...>
Row::As(
const std::initializer_list<size_type>& indexes)
const {
918 if (
sizeof...(T) != indexes.size()) {
919 throw FieldTupleMismatch(indexes.size(),
sizeof...(T));
921 std::tuple<T...> res;
922 detail::RowDataExtractor<T...>::ExtractTuple(*
this, indexes, res);
928 return AsSetOf<T>(kFieldTag);
933 detail::AssertSaneTypeToDeserialize<T>();
934 using ValueType = std::decay_t<T>;
935 io::traits::AssertIsValidRowType<ValueType>();
936 return TypedResultSet<T,
RowTag>{*
this};
941 detail::AssertSaneTypeToDeserialize<T>();
942 using ValueType = std::decay_t<T>;
943 detail::AssertRowTypeIsMappedToPgOrIsCompositeType<ValueType>();
944 if (FieldCount() > 1) {
947 return TypedResultSet<T,
FieldTag>{*
this};
950template <
typename Container>
952 detail::AssertSaneTypeToDeserialize<Container>();
953 using ValueType =
typename Container::value_type;
955 if constexpr (io::
traits::kCanReserve<Container>) {
958 auto res = AsSetOf<ValueType>();
960 auto inserter = io::
traits::Inserter(c);
961 auto row_it = res.begin();
962 for (std::size_t i = 0; i < res.Size(); ++i, ++row_it, ++inserter) {
969template <
typename Container>
971 detail::AssertSaneTypeToDeserialize<Container>();
972 using ValueType =
typename Container::value_type;
974 if constexpr (io::
traits::kCanReserve<Container>) {
977 auto res = AsSetOf<ValueType>(kRowTag);
979 auto inserter = io::
traits::Inserter(c);
980 auto row_it = res.begin();
981 for (std::size_t i = 0; i < res.Size(); ++i, ++row_it, ++inserter) {
990 return AsSingleRow<T>(kFieldTag);
995 detail::AssertSaneTypeToDeserialize<T>();
999 return Front().As<T>(kRowTag);
1002template <
typename T>
1004 detail::AssertSaneTypeToDeserialize<T>();
1008 return Front().As<T>(kFieldTag);
1011template <
typename T>
1013 return AsOptionalSingleRow<T>(kFieldTag);
1016template <
typename T>
1018 return IsEmpty() ? std::nullopt : std::optional<T>{AsSingleRow<T>(kRowTag)};
1021template <
typename T>
1023 return IsEmpty() ? std::nullopt : std::optional<T>{AsSingleRow<T>(kFieldTag)};
1028USERVER_NAMESPACE_END
1030#include <userver/storages/postgres/typed_result_set.hpp>