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();
245 std::string_view Name()
const;
246 const io::TypeBufferCategory& GetTypeBufferCategories()
const;
248 template <
typename T>
249 size_type ReadNullable(
const io::
FieldBuffer& fb, T&& val, std::true_type)
const {
250 using ValueType =
typename std::decay<T>::type;
253 NullSetter::SetNull(val);
255 Read(fb, std::forward<T>(val));
260 template <
typename T>
261 size_type ReadNullable(
const io::
FieldBuffer& buffer, T&& val, std::false_type)
const {
262 if (buffer.is_null) {
263 throw FieldValueIsNull{field_index_, Name(), val};
265 Read(buffer, std::forward<T>(val));
267 return buffer.length;
270 template <
typename T>
272 using ValueType =
typename std::decay<T>::type;
273 io::
traits::CheckParser<ValueType>();
275 io::ReadBuffer(buffer, std::forward<T>(val), GetTypeBufferCategories());
277 ex.AddMsgSuffix(fmt::format(
" (ResultSet error while reading field #{} name `{}`)", field_index_, Name()));
282 const detail::ResultWrapper& res_;
283 const size_type row_index_;
284 const size_type field_index_;
290 using size_type = std::size_t;
292 size_type RowIndex()
const {
return row_index_; }
293 size_type FieldIndex()
const {
return field_index_; }
301 Oid GetTypeOid()
const;
311 template <
typename T>
312 size_type
To(T&& val)
const {
313 return FieldView{*res_, row_index_, field_index_}.To(std::forward<T>(val));
318 template <
typename T>
319 void Coalesce(T& val,
const T& default_val)
const {
329 template <
typename T>
330 typename std::decay<T>::
type As()
const {
338 template <
typename T>
340 if (IsNull())
return default_val;
344 const io::TypeBufferCategory& GetTypeBufferCategories()
const;
351 Field(detail::ResultWrapperPtr res, size_type row, size_type col)
352 : res_{std::move(res)}, row_index_{row}, field_index_{col} {}
356 bool IsValid()
const;
357 int Compare(
const Field& rhs)
const;
358 std::ptrdiff_t Distance(
const Field& rhs)
const;
359 Field& Advance(std::ptrdiff_t);
363 detail::ResultWrapperPtr res_;
364 size_type row_index_{0};
365 size_type field_index_{0};
372 ConstFieldIterator() =
default;
377 ConstFieldIterator(detail::ResultWrapperPtr res, size_type row, size_type col)
378 : ConstDataIterator(std::move(res), row, col) {}
385 ReverseConstFieldIterator() =
default;
390 ReverseConstFieldIterator(detail::ResultWrapperPtr res, size_type row, size_type col)
391 : ConstDataIterator(std::move(res), row, col) {}
403 using size_type = std::size_t;
407 using value_type =
Field;
408 using reference =
Field;
409 using pointer = const_iterator;
412 size_type RowIndex()
const {
return row_index_; }
422 const_iterator cbegin()
const;
423 const_iterator begin()
const {
return cbegin(); }
424 const_iterator cend()
const;
425 const_iterator end()
const {
return cend(); }
429 const_reverse_iterator crbegin()
const;
430 const_reverse_iterator rbegin()
const {
return crbegin(); }
431 const_reverse_iterator crend()
const;
432 const_reverse_iterator rend()
const {
return crend(); }
441 reference
operator[](
const std::string& name)
const;
464 template <
typename T>
465 void To(T&& val)
const;
469 template <
typename T>
475 template <
typename T>
479 template <
typename... T>
480 void To(T&&... val)
const;
492 template <
typename T,
typename... Y>
497 template <
typename T>
506 template <
typename T>
514 template <
typename... T>
515 void To(
const std::initializer_list<std::string>& names, T&&... val)
const;
516 template <
typename... T>
517 std::tuple<T...> As(
const std::initializer_list<std::string>& names)
const;
521 template <
typename... T>
522 void To(
const std::initializer_list<size_type>& indexes, T&&... val)
const;
523 template <
typename... T>
524 std::tuple<T...> As(
const std::initializer_list<size_type>& indexes)
const;
527 size_type IndexOfName(
const std::string&)
const;
529 FieldView GetFieldView(size_type index)
const;
536 Row(detail::ResultWrapperPtr res, size_type row) : res_{std::move(res)}, row_index_{row} {}
540 bool IsValid()
const;
541 int Compare(
const Row& rhs)
const;
542 std::ptrdiff_t Distance(
const Row& rhs)
const;
543 Row& Advance(std::ptrdiff_t);
546 detail::ResultWrapperPtr res_;
547 size_type row_index_{0};
553 ConstRowIterator() =
default;
558 ConstRowIterator(detail::ResultWrapperPtr res, size_type row) : ConstDataIterator(std::move(res), row) {}
565 ReverseConstRowIterator() =
default;
570 ReverseConstRowIterator(detail::ResultWrapperPtr res, size_type row) : ConstDataIterator(std::move(res), row) {}
588 using size_type = std::size_t;
589 using difference_type = std::ptrdiff_t;
590 static constexpr size_type npos = std::numeric_limits<size_type>::max();
597 using value_type =
Row;
598 using reference = value_type;
599 using pointer = const_iterator;
602 explicit ResultSet(std::shared_ptr<detail::ResultWrapper> pimpl) : pimpl_{std::move(pimpl)} {}
606 bool IsEmpty()
const {
return Size() == 0; }
608 size_type RowsAffected()
const;
609 std::string CommandStatus()
const;
615 const_iterator cbegin()
const&;
616 const_iterator begin()
const& {
return cbegin(); }
617 const_iterator cend()
const&;
618 const_iterator end()
const& {
return cend(); }
621 const_iterator cbegin()
const&& =
delete;
622 const_iterator begin()
const&& =
delete;
623 const_iterator cend()
const&& =
delete;
624 const_iterator end()
const&& =
delete;
628 const_reverse_iterator crbegin()
const&;
629 const_reverse_iterator rbegin()
const& {
return crbegin(); }
630 const_reverse_iterator crend()
const&;
631 const_reverse_iterator rend()
const& {
return crend(); }
633 const_reverse_iterator crbegin()
const&& =
delete;
634 const_reverse_iterator rbegin()
const&& =
delete;
635 const_reverse_iterator crend()
const&& =
delete;
636 const_reverse_iterator rend()
const&& =
delete;
639 reference Front()
const&;
640 reference Back()
const&;
642 reference Front()
const&& =
delete;
643 reference Back()
const&& =
delete;
649 reference operator[](size_type index)
const&& =
delete;
655 size_type FieldCount()
const;
665 template <
typename T>
667 template <
typename T>
668 auto AsSetOf(
RowTag)
const;
669 template <
typename T>
674 template <
typename Container>
676 template <
typename Container>
677 Container AsContainer(
RowTag)
const;
682 template <
typename T>
684 template <
typename T>
685 auto AsSingleRow(
RowTag)
const;
686 template <
typename T>
693 template <
typename T>
695 template <
typename T>
696 std::optional<T> AsOptionalSingleRow(
RowTag)
const;
697 template <
typename T>
698 std::optional<T> AsOptionalSingleRow(
FieldTag)
const;
701 friend class detail::ConnectionImpl;
702 void FillBufferCategories(
const UserTypes& types);
703 void SetBufferCategoriesFrom(
const ResultSet&);
705 template <
typename T,
typename Tag>
706 friend class TypedResultSet;
707 friend class ConnectionImpl;
709 std::shared_ptr<detail::ResultWrapper> pimpl_;
715struct IsOptionalFromOptional : std::false_type {};
718struct IsOptionalFromOptional<std::optional<std::optional<T>>> : std::true_type {};
721struct IsOneVariant : std::false_type {};
724struct IsOneVariant<std::variant<T>> : std::true_type {};
726template <
typename... Args>
727constexpr void AssertSaneTypeToDeserialize() {
729 !(IsOptionalFromOptional<std::remove_const_t<std::remove_reference_t<Args>>>::value || ...),
730 "Attempt to get an optional<optional<T>> was detected. Such "
731 "optional-from-optional types are very error prone, obfuscate code and "
732 "are ambiguous to deserialize. Change the type to just optional<T>"
735 !(IsOneVariant<std::remove_const_t<std::remove_reference_t<Args>>>::value || ...),
736 "Attempt to get an variant<T> was detected. Such variant from one type "
737 "obfuscates code. Change the type to just T"
743template <
typename IndexTuple,
typename... T>
744struct RowDataExtractorBase;
746template <std::size_t... Indexes,
typename... T>
747struct RowDataExtractorBase<std::index_sequence<Indexes...>, T...> {
748 static void ExtractValues(
const Row& row, T&&... val) {
749 static_assert(
sizeof...(Indexes) ==
sizeof...(T));
751 std::size_t field_index = 0;
752 const auto perform = [&](
auto&& arg) { row.GetFieldView(field_index++).To(std::forward<
decltype(arg)>(arg)); };
753 (perform(std::forward<T>(val)), ...);
755 static void ExtractTuple(
const Row& row, std::tuple<T...>& val) {
756 static_assert(
sizeof...(Indexes) ==
sizeof...(T));
758 std::size_t field_index = 0;
759 const auto perform = [&](
auto& arg) { row.GetFieldView(field_index++).To(arg); };
760 (perform(std::get<Indexes>(val)), ...);
762 static void ExtractTuple(
const Row& row, std::tuple<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(arg); };
767 (perform(std::get<Indexes>(val)), ...);
770 static void ExtractValues(
const Row& row,
const std::initializer_list<std::string>& names, T&&... val) {
771 (row[*(names.begin() + Indexes)].To(std::forward<T>(val)), ...);
773 static void ExtractTuple(
const Row& row,
const std::initializer_list<std::string>& names, std::tuple<T...>& val) {
774 std::tuple<T...> tmp{row[*(names.begin() + Indexes)].
template As<T>()...};
778 static void ExtractValues(
const Row& row,
const std::initializer_list<std::size_t>& indexes, T&&... val) {
779 (row[*(indexes.begin() + Indexes)].To(std::forward<T>(val)), ...);
781 static void ExtractTuple(
const Row& row,
const std::initializer_list<std::size_t>& indexes, std::tuple<T...>& val) {
782 std::tuple<T...> tmp{row[*(indexes.begin() + Indexes)].
template As<T>()...};
787template <
typename... T>
788struct RowDataExtractor : RowDataExtractorBase<std::index_sequence_for<T...>, T...> {};
791struct TupleDataExtractor;
792template <
typename... T>
793struct TupleDataExtractor<std::tuple<T...>> : RowDataExtractorBase<std::index_sequence_for<T...>, T...> {};
796template <
typename RowType>
797constexpr void AssertRowTypeIsMappedToPgOrIsCompositeType() {
800 io::
traits::kIsMappedToPg<RowType> ||
io::
traits::kIsCompositeType<RowType>,
801 "Row type must be mapped to pg type(CppToUserPg) or one of the "
803 "1. primitive type. "
805 "3. Aggregation type. See std::aggregation. "
806 "4. Has a Introspect method that makes the std::tuple from your "
808 "For more info see `uPg: Typed PostgreSQL results` chapter in docs."
816 To(std::forward<T>(val), kFieldTag);
821 detail::AssertSaneTypeToDeserialize<T>();
823 using ValueType = std::decay_t<T>;
824 io::traits::AssertIsValidRowType<ValueType>();
825 using RowType = io::RowType<ValueType>;
826 using TupleType =
typename RowType::TupleType;
827 constexpr auto tuple_size = RowType::size;
828 if (tuple_size > Size()) {
829 throw InvalidTupleSizeRequested(Size(), tuple_size);
830 }
else if (tuple_size < Size()) {
836 detail::TupleDataExtractor<TupleType>::ExtractTuple(*
this, RowType::GetTuple(std::forward<T>(val)));
841 detail::AssertSaneTypeToDeserialize<T>();
842 using ValueType = std::decay_t<T>;
843 detail::AssertRowTypeIsMappedToPgOrIsCompositeType<ValueType>();
851 (*
this)
[0
].To(std::forward<T>(val));
854template <
typename... T>
856 detail::AssertSaneTypeToDeserialize<T...>();
857 if (
sizeof...(T) >
Size()) {
860 detail::RowDataExtractor<T...>::ExtractValues(*
this, std::forward<T>(val)...);
863template <
typename T,
typename... Y>
865 if constexpr (
sizeof...(Y) > 0) {
866 std::tuple<T, Y...> res;
870 return As<T>(kFieldTag);
874template <
typename... T>
875void Row::
To(
const std::initializer_list<std::string>& names, T&&... val)
const {
876 detail::AssertSaneTypeToDeserialize<T...>();
877 if (
sizeof...(T) != names.size()) {
878 throw FieldTupleMismatch(names.size(),
sizeof...(T));
880 detail::RowDataExtractor<T...>::ExtractValues(*
this, names, std::forward<T>(val)...);
883template <
typename... T>
884std::tuple<T...>
Row::As(
const std::initializer_list<std::string>& names)
const {
885 if (
sizeof...(T) != names.size()) {
886 throw FieldTupleMismatch(names.size(),
sizeof...(T));
888 std::tuple<T...> res;
889 detail::RowDataExtractor<T...>::ExtractTuple(*
this, names, res);
893template <
typename... T>
894void Row::
To(
const std::initializer_list<size_type>& indexes, T&&... val)
const {
895 detail::AssertSaneTypeToDeserialize<T...>();
896 if (
sizeof...(T) != indexes.size()) {
897 throw FieldTupleMismatch(indexes.size(),
sizeof...(T));
899 detail::RowDataExtractor<T...>::ExtractValues(*
this, indexes, std::forward<T>(val)...);
902template <
typename... T>
903std::tuple<T...>
Row::As(
const std::initializer_list<size_type>& indexes)
const {
904 if (
sizeof...(T) != indexes.size()) {
905 throw FieldTupleMismatch(indexes.size(),
sizeof...(T));
907 std::tuple<T...> res;
908 detail::RowDataExtractor<T...>::ExtractTuple(*
this, indexes, res);
914 return AsSetOf<T>(kFieldTag);
919 detail::AssertSaneTypeToDeserialize<T>();
920 using ValueType = std::decay_t<T>;
921 io::traits::AssertIsValidRowType<ValueType>();
922 return TypedResultSet<T,
RowTag>{*
this};
927 detail::AssertSaneTypeToDeserialize<T>();
928 using ValueType = std::decay_t<T>;
929 detail::AssertRowTypeIsMappedToPgOrIsCompositeType<ValueType>();
930 if (FieldCount() > 1) {
933 return TypedResultSet<T,
FieldTag>{*
this};
936template <
typename Container>
938 detail::AssertSaneTypeToDeserialize<Container>();
939 using ValueType =
typename Container::value_type;
941 if constexpr (
io::
traits::kCanReserve<Container>) {
944 auto res = AsSetOf<ValueType>();
946 auto inserter =
io::
traits::Inserter(c);
947 auto row_it = res.begin();
948 for (std::size_t i = 0; i < res.Size(); ++i, ++row_it, ++inserter) {
955template <
typename Container>
957 detail::AssertSaneTypeToDeserialize<Container>();
958 using ValueType =
typename Container::value_type;
960 if constexpr (
io::
traits::kCanReserve<Container>) {
963 auto res = AsSetOf<ValueType>(kRowTag);
965 auto inserter =
io::
traits::Inserter(c);
966 auto row_it = res.begin();
967 for (std::size_t i = 0; i < res.Size(); ++i, ++row_it, ++inserter) {
976 return AsSingleRow<T>(kFieldTag);
981 detail::AssertSaneTypeToDeserialize<T>();
985 return Front().As<T>(kRowTag);
990 detail::AssertSaneTypeToDeserialize<T>();
994 return Front().As<T>(kFieldTag);
999 return AsOptionalSingleRow<T>(kFieldTag);
1002template <
typename T>
1004 return IsEmpty() ? std::nullopt : std::optional<T>{AsSingleRow<T>(kRowTag)};
1007template <
typename T>
1009 return IsEmpty() ? std::nullopt : std::optional<T>{AsSingleRow<T>(kFieldTag)};
1014USERVER_NAMESPACE_END
1016#include <userver/storages/postgres/typed_result_set.hpp>