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;
322 size_type Length()
const;
327 template <
typename T>
328 size_type
To(T&& val)
const {
329 return FieldView{*res_, row_index_, field_index_}.To(std::forward<T>(val));
334 template <
typename T>
335 void Coalesce(T& val,
const T& default_val)
const {
345 template <
typename T>
346 typename std::decay<T>::
type As()
const {
354 template <
typename T>
356 if (IsNull())
return default_val;
360 const io::TypeBufferCategory& GetTypeBufferCategories()
const;
367 Field(detail::ResultWrapperPtr res, size_type row, size_type col)
368 : res_{std::move(res)}, row_index_{row}, field_index_{col} {}
372 bool IsValid()
const;
373 int Compare(
const Field& rhs)
const;
374 std::ptrdiff_t Distance(
const Field& rhs)
const;
375 Field& Advance(std::ptrdiff_t);
379 detail::ResultWrapperPtr res_;
380 size_type row_index_{0};
381 size_type field_index_{0};
388 ConstFieldIterator() =
default;
393 ConstFieldIterator(detail::ResultWrapperPtr res, size_type row, size_type col)
394 : ConstDataIterator(std::move(res), row, col) {}
401 ReverseConstFieldIterator() =
default;
406 ReverseConstFieldIterator(detail::ResultWrapperPtr res, size_type row, size_type col)
407 : ConstDataIterator(std::move(res), row, col) {}
419 using size_type = std::size_t;
423 using value_type =
Field;
424 using reference =
Field;
425 using pointer = const_iterator;
428 size_type RowIndex()
const {
return row_index_; }
438 const_iterator cbegin()
const;
439 const_iterator begin()
const {
return cbegin(); }
440 const_iterator cend()
const;
441 const_iterator end()
const {
return cend(); }
445 const_reverse_iterator crbegin()
const;
446 const_reverse_iterator rbegin()
const {
return crbegin(); }
447 const_reverse_iterator crend()
const;
448 const_reverse_iterator rend()
const {
return crend(); }
457 reference
operator[](
const std::string& name)
const;
480 template <
typename T>
481 void To(T&& val)
const;
485 template <
typename T>
491 template <
typename T>
495 template <
typename... T>
496 void To(T&&... val)
const;
508 template <
typename T,
typename... Y>
513 template <
typename T>
522 template <
typename T>
530 template <
typename... T>
531 void To(
const std::initializer_list<std::string>& names, T&&... val)
const;
532 template <
typename... T>
533 std::tuple<T...> As(
const std::initializer_list<std::string>& names)
const;
537 template <
typename... T>
538 void To(
const std::initializer_list<size_type>& indexes, T&&... val)
const;
539 template <
typename... T>
540 std::tuple<T...> As(
const std::initializer_list<size_type>& indexes)
const;
543 size_type IndexOfName(
const std::string&)
const;
545 FieldView GetFieldView(size_type index)
const;
552 Row(detail::ResultWrapperPtr res, size_type row) : res_{std::move(res)}, row_index_{row} {}
556 bool IsValid()
const;
557 int Compare(
const Row& rhs)
const;
558 std::ptrdiff_t Distance(
const Row& rhs)
const;
559 Row& Advance(std::ptrdiff_t);
562 detail::ResultWrapperPtr res_;
563 size_type row_index_{0};
569 ConstRowIterator() =
default;
574 ConstRowIterator(detail::ResultWrapperPtr res, size_type row) : ConstDataIterator(std::move(res), row) {}
581 ReverseConstRowIterator() =
default;
586 ReverseConstRowIterator(detail::ResultWrapperPtr res, size_type row) : ConstDataIterator(std::move(res), row) {}
604 using size_type = std::size_t;
605 using difference_type = std::ptrdiff_t;
606 static constexpr size_type npos = std::numeric_limits<size_type>::max();
613 using value_type =
Row;
614 using reference = value_type;
615 using pointer = const_iterator;
618 explicit ResultSet(std::shared_ptr<detail::ResultWrapper> pimpl) : pimpl_{std::move(pimpl)} {}
622 bool IsEmpty()
const {
return Size() == 0; }
624 size_type RowsAffected()
const;
625 std::string CommandStatus()
const;
631 const_iterator cbegin()
const&;
632 const_iterator begin()
const& {
return cbegin(); }
633 const_iterator cend()
const&;
634 const_iterator end()
const& {
return cend(); }
637 const_iterator cbegin()
const&& =
delete;
638 const_iterator begin()
const&& =
delete;
639 const_iterator cend()
const&& =
delete;
640 const_iterator end()
const&& =
delete;
644 const_reverse_iterator crbegin()
const&;
645 const_reverse_iterator rbegin()
const& {
return crbegin(); }
646 const_reverse_iterator crend()
const&;
647 const_reverse_iterator rend()
const& {
return crend(); }
649 const_reverse_iterator crbegin()
const&& =
delete;
650 const_reverse_iterator rbegin()
const&& =
delete;
651 const_reverse_iterator crend()
const&& =
delete;
652 const_reverse_iterator rend()
const&& =
delete;
655 reference Front()
const&;
656 reference Back()
const&;
658 reference Front()
const&& =
delete;
659 reference Back()
const&& =
delete;
665 reference operator[](size_type index)
const&& =
delete;
671 size_type FieldCount()
const;
681 template <
typename T>
683 template <
typename T>
684 auto AsSetOf(
RowTag)
const;
685 template <
typename T>
690 template <
typename Container>
692 template <
typename Container>
693 Container AsContainer(
RowTag)
const;
698 template <
typename T>
700 template <
typename T>
701 auto AsSingleRow(
RowTag)
const;
702 template <
typename T>
709 template <
typename T>
711 template <
typename T>
712 std::optional<T> AsOptionalSingleRow(
RowTag)
const;
713 template <
typename T>
714 std::optional<T> AsOptionalSingleRow(
FieldTag)
const;
717 friend class detail::ConnectionImpl;
718 void FillBufferCategories(
const UserTypes& types);
719 void SetBufferCategoriesFrom(
const ResultSet&);
721 template <
typename T,
typename Tag>
722 friend class TypedResultSet;
723 friend class ConnectionImpl;
725 std::shared_ptr<detail::ResultWrapper> pimpl_;
731struct IsOptionalFromOptional : std::false_type {};
734struct IsOptionalFromOptional<std::optional<std::optional<T>>> : std::true_type {};
737struct IsOneVariant : std::false_type {};
740struct IsOneVariant<std::variant<T>> : std::true_type {};
742template <
typename... Args>
743constexpr void AssertSaneTypeToDeserialize() {
745 !(IsOptionalFromOptional<std::remove_const_t<std::remove_reference_t<Args>>>::value || ...),
746 "Attempt to get an optional<optional<T>> was detected. Such "
747 "optional-from-optional types are very error prone, obfuscate code and "
748 "are ambiguous to deserialize. Change the type to just optional<T>"
751 !(IsOneVariant<std::remove_const_t<std::remove_reference_t<Args>>>::value || ...),
752 "Attempt to get an variant<T> was detected. Such variant from one type "
753 "obfuscates code. Change the type to just T"
759template <
typename IndexTuple,
typename... T>
760struct RowDataExtractorBase;
762template <std::size_t... Indexes,
typename... T>
763struct RowDataExtractorBase<std::index_sequence<Indexes...>, T...> {
764 static void ExtractValues(
const Row& row, T&&... val) {
765 static_assert(
sizeof...(Indexes) ==
sizeof...(T));
767 std::size_t field_index = 0;
768 const auto perform = [&](
auto&& arg) { row.GetFieldView(field_index++).To(std::forward<
decltype(arg)>(arg)); };
769 (perform(std::forward<T>(val)), ...);
771 static void ExtractTuple(
const Row& row, std::tuple<T...>& val) {
772 static_assert(
sizeof...(Indexes) ==
sizeof...(T));
774 std::size_t field_index = 0;
775 const auto perform = [&](
auto& arg) { row.GetFieldView(field_index++).To(arg); };
776 (perform(std::get<Indexes>(val)), ...);
778 static void ExtractTuple(
const Row& row, std::tuple<T...>&& val) {
779 static_assert(
sizeof...(Indexes) ==
sizeof...(T));
781 std::size_t field_index = 0;
782 const auto perform = [&](
auto& arg) { row.GetFieldView(field_index++).To(arg); };
783 (perform(std::get<Indexes>(val)), ...);
786 static void ExtractValues(
const Row& row,
const std::initializer_list<std::string>& names, T&&... val) {
787 (row[*(names.begin() + Indexes)].To(std::forward<T>(val)), ...);
789 static void ExtractTuple(
const Row& row,
const std::initializer_list<std::string>& names, std::tuple<T...>& val) {
790 std::tuple<T...> tmp{row[*(names.begin() + Indexes)].
template As<T>()...};
794 static void ExtractValues(
const Row& row,
const std::initializer_list<std::size_t>& indexes, T&&... val) {
795 (row[*(indexes.begin() + Indexes)].To(std::forward<T>(val)), ...);
797 static void ExtractTuple(
const Row& row,
const std::initializer_list<std::size_t>& indexes, std::tuple<T...>& val) {
798 std::tuple<T...> tmp{row[*(indexes.begin() + Indexes)].
template As<T>()...};
803template <
typename... T>
804struct RowDataExtractor : RowDataExtractorBase<std::index_sequence_for<T...>, T...> {};
807struct TupleDataExtractor;
808template <
typename... T>
809struct TupleDataExtractor<std::tuple<T...>> : RowDataExtractorBase<std::index_sequence_for<T...>, T...> {};
812template <
typename RowType>
813constexpr void AssertRowTypeIsMappedToPgOrIsCompositeType() {
816 io::
traits::kIsMappedToPg<RowType> || io::
traits::kIsCompositeType<RowType>,
817 "Row type must be mapped to pg type(CppToUserPg) or one of the "
819 "1. primitive type. "
821 "3. Aggregation type. See std::aggregation. "
822 "4. Has a Introspect method that makes the std::tuple from your "
824 "For more info see `uPg: Typed PostgreSQL results` chapter in docs."
832 To(std::forward<T>(val), kFieldTag);
837 detail::AssertSaneTypeToDeserialize<T>();
839 using ValueType = std::decay_t<T>;
840 io::traits::AssertIsValidRowType<ValueType>();
841 using RowType = io::RowType<ValueType>;
842 using TupleType =
typename RowType::TupleType;
843 constexpr auto tuple_size = RowType::size;
844 if (tuple_size > Size()) {
845 throw InvalidTupleSizeRequested(Size(), tuple_size);
846 }
else if (tuple_size < Size()) {
849 << compiler::GetTypeName<T>();
852 detail::TupleDataExtractor<TupleType>::ExtractTuple(*
this, RowType::GetTuple(std::forward<T>(val)));
857 detail::AssertSaneTypeToDeserialize<T>();
858 using ValueType = std::decay_t<T>;
859 detail::AssertRowTypeIsMappedToPgOrIsCompositeType<ValueType>();
865 throw NonSingleColumnResultSet{Size(), compiler::GetTypeName<T>(),
"As"};
867 (*
this)
[0
].To(std::forward<T>(val));
870template <
typename... T>
872 detail::AssertSaneTypeToDeserialize<T...>();
873 if (
sizeof...(T) >
Size()) {
876 detail::RowDataExtractor<T...>::ExtractValues(*
this, std::forward<T>(val)...);
879template <
typename T,
typename... Y>
881 if constexpr (
sizeof...(Y) > 0) {
882 std::tuple<T, Y...> res;
886 return As<T>(kFieldTag);
890template <
typename... T>
891void Row::
To(
const std::initializer_list<std::string>& names, T&&... val)
const {
892 detail::AssertSaneTypeToDeserialize<T...>();
893 if (
sizeof...(T) != names.size()) {
894 throw FieldTupleMismatch(names.size(),
sizeof...(T));
896 detail::RowDataExtractor<T...>::ExtractValues(*
this, names, std::forward<T>(val)...);
899template <
typename... T>
900std::tuple<T...>
Row::As(
const std::initializer_list<std::string>& names)
const {
901 if (
sizeof...(T) != names.size()) {
902 throw FieldTupleMismatch(names.size(),
sizeof...(T));
904 std::tuple<T...> res;
905 detail::RowDataExtractor<T...>::ExtractTuple(*
this, names, res);
909template <
typename... T>
910void Row::
To(
const std::initializer_list<size_type>& indexes, T&&... val)
const {
911 detail::AssertSaneTypeToDeserialize<T...>();
912 if (
sizeof...(T) != indexes.size()) {
913 throw FieldTupleMismatch(indexes.size(),
sizeof...(T));
915 detail::RowDataExtractor<T...>::ExtractValues(*
this, indexes, std::forward<T>(val)...);
918template <
typename... T>
919std::tuple<T...>
Row::As(
const std::initializer_list<size_type>& indexes)
const {
920 if (
sizeof...(T) != indexes.size()) {
921 throw FieldTupleMismatch(indexes.size(),
sizeof...(T));
923 std::tuple<T...> res;
924 detail::RowDataExtractor<T...>::ExtractTuple(*
this, indexes, res);
930 return AsSetOf<T>(kFieldTag);
935 detail::AssertSaneTypeToDeserialize<T>();
936 using ValueType = std::decay_t<T>;
937 io::traits::AssertIsValidRowType<ValueType>();
938 return TypedResultSet<T,
RowTag>{*
this};
943 detail::AssertSaneTypeToDeserialize<T>();
944 using ValueType = std::decay_t<T>;
945 detail::AssertRowTypeIsMappedToPgOrIsCompositeType<ValueType>();
946 if (FieldCount() > 1) {
947 throw NonSingleColumnResultSet{FieldCount(), compiler::GetTypeName<T>(),
"AsSetOf"};
949 return TypedResultSet<T,
FieldTag>{*
this};
952template <
typename Container>
954 detail::AssertSaneTypeToDeserialize<Container>();
955 using ValueType =
typename Container::value_type;
957 if constexpr (io::
traits::kCanReserve<Container>) {
960 auto res = AsSetOf<ValueType>();
962 auto inserter = io::
traits::Inserter(c);
963 auto row_it = res.begin();
964 for (std::size_t i = 0; i < res.Size(); ++i, ++row_it, ++inserter) {
971template <
typename Container>
973 detail::AssertSaneTypeToDeserialize<Container>();
974 using ValueType =
typename Container::value_type;
976 if constexpr (io::
traits::kCanReserve<Container>) {
979 auto res = AsSetOf<ValueType>(kRowTag);
981 auto inserter = io::
traits::Inserter(c);
982 auto row_it = res.begin();
983 for (std::size_t i = 0; i < res.Size(); ++i, ++row_it, ++inserter) {
992 return AsSingleRow<T>(kFieldTag);
997 detail::AssertSaneTypeToDeserialize<T>();
1001 return Front().As<T>(kRowTag);
1004template <
typename T>
1006 detail::AssertSaneTypeToDeserialize<T>();
1010 return Front().As<T>(kFieldTag);
1013template <
typename T>
1015 return AsOptionalSingleRow<T>(kFieldTag);
1018template <
typename T>
1020 return IsEmpty() ? std::nullopt : std::optional<T>{AsSingleRow<T>(kRowTag)};
1023template <
typename T>
1025 return IsEmpty() ? std::nullopt : std::optional<T>{AsSingleRow<T>(kFieldTag)};
1030USERVER_NAMESPACE_END
1032#include <userver/storages/postgres/typed_result_set.hpp>