6#include <initializer_list>
13#include <userver/storages/postgres/field.hpp>
14#include <userver/storages/postgres/io/supported_types.hpp>
15#include <userver/utils/zstring_view.hpp>
17#include <userver/logging/log.hpp>
19USERVER_NAMESPACE_BEGIN
24template <
typename T,
typename ExtractionTag>
30 RowDescription(detail::ResultWrapperPtr res) : res_{std::move(res)} {}
38 detail::ResultWrapperPtr res_;
50 using size_type = std::size_t;
54 using value_type =
Field;
55 using reference =
Field;
56 using pointer = const_iterator;
59 size_type RowIndex()
const {
return row_index_; }
69 const_iterator cbegin()
const;
70 const_iterator begin()
const {
return cbegin(); }
71 const_iterator cend()
const;
72 const_iterator end()
const {
return cend(); }
76 const_reverse_iterator crbegin()
const;
77 const_reverse_iterator rbegin()
const {
return crbegin(); }
78 const_reverse_iterator crend()
const;
79 const_reverse_iterator rend()
const {
return crend(); }
111 template <
typename T>
112 void To(T&& val)
const;
116 template <
typename T>
122 template <
typename T>
126 template <
typename... T>
127 void To(T&&... val)
const;
139 template <
typename T,
typename... Y>
144 template <
typename T>
153 template <
typename T>
161 template <
typename... T>
162 void To(
const std::initializer_list<USERVER_NAMESPACE::
utils::
zstring_view>& names, T&&... val)
const;
163 template <
typename... T>
164 std::tuple<T...> As(
const std::initializer_list<USERVER_NAMESPACE::
utils::
zstring_view>& names)
const;
168 template <
typename... T>
169 void To(
const std::initializer_list<size_type>& indexes, T&&... val)
const;
170 template <
typename... T>
171 std::tuple<T...> As(
const std::initializer_list<size_type>& indexes)
const;
176 FieldView GetFieldView(size_type index)
const;
181 template <
typename T,
typename Tag>
182 friend class TypedResultSet;
186 Row(detail::ResultWrapperPtr res, size_type row) : res_{std::move(res)}, row_index_{row} {}
190 bool IsValid()
const;
191 int Compare(
const Row& rhs)
const;
192 std::ptrdiff_t Distance(
const Row& rhs)
const;
193 Row& Advance(std::ptrdiff_t);
196 detail::ResultWrapperPtr res_;
197 size_type row_index_{0};
203 ConstRowIterator() =
default;
208 ConstRowIterator(detail::ResultWrapperPtr res, size_type row) : ConstDataIterator(std::move(res), row) {}
215 ReverseConstRowIterator() =
default;
220 ReverseConstRowIterator(detail::ResultWrapperPtr res, size_type row) : ConstDataIterator(std::move(res), row) {}
226struct IsOptionalFromOptional : std::false_type {};
229struct IsOptionalFromOptional<std::optional<std::optional<T>>> : std::true_type {};
232struct IsOneVariant : std::false_type {};
235struct IsOneVariant<std::variant<T>> : std::true_type {};
237template <
typename... Args>
238constexpr void AssertSaneTypeToDeserialize() {
240 !(IsOptionalFromOptional<std::remove_const_t<std::remove_reference_t<Args>>>::value || ...),
241 "Attempt to get an optional<optional<T>> was detected. Such "
242 "optional-from-optional types are very error prone, obfuscate code and "
243 "are ambiguous to deserialize. Change the type to just optional<T>"
246 !(IsOneVariant<std::remove_const_t<std::remove_reference_t<Args>>>::value || ...),
247 "Attempt to get an variant<T> was detected. Such variant from one type "
248 "obfuscates code. Change the type to just T"
254template <
typename IndexTuple,
typename... T>
255struct RowDataExtractorBase;
257template <std::size_t... Indexes,
typename... T>
258struct RowDataExtractorBase<std::index_sequence<Indexes...>, T...> {
259 static void ExtractValues(
const Row& row, T&&... val) {
260 static_assert(
sizeof...(Indexes) ==
sizeof...(T));
262 std::size_t field_index = 0;
263 const auto perform = [&](
auto&& arg) { row.GetFieldView(field_index++).To(std::forward<
decltype(arg)>(arg)); };
264 (perform(std::forward<T>(val)), ...);
266 static void ExtractTuple(
const Row& row, std::tuple<T...>& val) {
267 static_assert(
sizeof...(Indexes) ==
sizeof...(T));
269 std::size_t field_index = 0;
270 const auto perform = [&](
auto& arg) { row.GetFieldView(field_index++).To(arg); };
271 (perform(std::get<Indexes>(val)), ...);
273 static void ExtractTuple(
const Row& row, std::tuple<T...>&& val) {
274 static_assert(
sizeof...(Indexes) ==
sizeof...(T));
276 std::size_t field_index = 0;
277 const auto perform = [&](
auto& arg) { row.GetFieldView(field_index++).To(arg); };
278 (perform(std::get<Indexes>(val)), ...);
281 static void ExtractValues(
286 (row
[*(names.begin() +
Indexes)
].To(std::forward<T>(val)), ...);
288 static void ExtractTuple(
291 std::tuple<T...>& val
293 std::tuple<T...> tmp{row
[*(names.begin() +
Indexes)
].
template As<T>()...};
297 static void ExtractValues(
const Row& row,
const std::initializer_list<std::size_t>& indexes, T&&... val) {
298 (row
[*(indexes.begin() +
Indexes)
].To(std::forward<T>(val)), ...);
300 static void ExtractTuple(
const Row& row,
const std::initializer_list<std::size_t>& indexes, std::tuple<T...>& val) {
301 std::tuple<T...> tmp{row
[*(indexes.begin() +
Indexes)
].
template As<T>()...};
306template <
typename... T>
307struct RowDataExtractor : RowDataExtractorBase<std::index_sequence_for<T...>, T...> {};
310struct TupleDataExtractor;
311template <
typename... T>
312struct TupleDataExtractor<std::tuple<T...>> : RowDataExtractorBase<std::index_sequence_for<T...>, T...> {};
315template <
typename RowType>
316constexpr void AssertRowTypeIsMappedToPgOrIsCompositeType() {
319 io::
traits::kIsMappedToPg<RowType> || io::
traits::kIsCompositeType<RowType>,
320 "Row type must be mapped to pg type(CppToUserPg) or one of the "
322 "1. primitive type. "
324 "3. Aggregation type. See std::aggregation. "
325 "4. Has a Introspect method that makes the std::tuple from your "
327 "For more info see `uPg: Typed PostgreSQL results` chapter in docs."
335 To(std::forward<T>(val), kFieldTag);
340 detail::AssertSaneTypeToDeserialize<T>();
342 using ValueType = std::decay_t<T>;
343 io::
traits::AssertIsValidRowType<ValueType>();
344 using RowType = io::
RowType<ValueType>;
345 using TupleType =
typename RowType::TupleType;
346 constexpr auto tuple_size = RowType::size;
347 if (tuple_size >
Size()) {
349 }
else if (tuple_size <
Size()) {
355 detail::TupleDataExtractor<TupleType>::ExtractTuple(*
this, RowType::GetTuple(std::forward<T>(val)));
360 detail::AssertSaneTypeToDeserialize<T>();
361 using ValueType = std::decay_t<T>;
362 detail::AssertRowTypeIsMappedToPgOrIsCompositeType<ValueType>();
370 (*
this)
[0
].To(std::forward<T>(val));
373template <
typename... T>
375 detail::AssertSaneTypeToDeserialize<T...>();
376 if (
sizeof...(T) >
Size()) {
379 detail::RowDataExtractor<T...>::ExtractValues(*
this, std::forward<T>(val)...);
382template <
typename T,
typename... Y>
384 if constexpr (
sizeof...(Y) > 0) {
385 std::tuple<T, Y...> res;
389 return As<T>(kFieldTag);
393template <
typename... T>
395 detail::AssertSaneTypeToDeserialize<T...>();
396 if (
sizeof...(T) != names.size()) {
399 detail::RowDataExtractor<T...>::ExtractValues(*
this, names, std::forward<T>(val)...);
402template <
typename... T>
403std::tuple<T...>
Row::As(
const std::initializer_list<USERVER_NAMESPACE::
utils::
zstring_view>& names)
const {
404 if (
sizeof...(T) != names.size()) {
407 std::tuple<T...> res;
408 detail::RowDataExtractor<T...>::ExtractTuple(*
this, names, res);
412template <
typename... T>
413void Row::
To(
const std::initializer_list<size_type>& indexes, T&&... val)
const {
414 detail::AssertSaneTypeToDeserialize<T...>();
415 if (
sizeof...(T) != indexes.size()) {
418 detail::RowDataExtractor<T...>::ExtractValues(*
this, indexes, std::forward<T>(val)...);
421template <
typename... T>
422std::tuple<T...>
Row::As(
const std::initializer_list<size_type>& indexes)
const {
423 if (
sizeof...(T) != indexes.size()) {
426 std::tuple<T...> res;
427 detail::RowDataExtractor<T...>::ExtractTuple(*
this, indexes, res);