3#include <boost/pfr/core.hpp>
4#include <boost/pfr/traits.hpp>
6#include <userver/storages/postgres/detail/is_in_namespace.hpp>
7#include <userver/storages/postgres/io/type_traits.hpp>
9#include <userver/utils/strong_typedef.hpp>
12#include <userver/utils/text_light.hpp>
14USERVER_NAMESPACE_BEGIN
31inline constexpr RowTag kRowTag{};
32inline constexpr FieldTag kFieldTag{};
41template <
typename... T>
42struct IsTuple<std::tuple<T...>> : std::true_type {};
47struct HasConstIntrospection : std::false_type {};
50requires requires(
const T& t) { t.Introspect(); }
51struct HasConstIntrospection<T> : std::true_type {};
54struct HasNonConstIntrospection : std::false_type {
56 !impl::HasConstIntrospection<T>::value,
57 "PostgreSQL driver requires non-const Introspect(). "
58 "Example: auto Introspect() { return std::tie(a, b, c, d); }"
63requires requires(T& t) { t.Introspect(); }
64struct HasNonConstIntrospection<T> : std::true_type {
66 IsTuple<
decltype(std::declval<T&>().Introspect())>::value,
67 "Introspect() should return a std::tuple. "
68 "Example: auto Introspect() { return std::tie(a, b, c, d); }"
76 static_assert(!std::is_const_v<T>);
77 static_assert(!std::is_reference_v<T>);
82struct ForDeserializationTag;
85struct IsPostgresBuildInTypeWrapperImpl : std::false_type {};
88requires requires { T::kIsPostgresBuildInTypeWrapper; }
89struct IsPostgresBuildInTypeWrapperImpl<T> : std::integral_constant<
const bool, T::kIsPostgresBuildInTypeWrapper> {
91 std::is_same_v<
decltype(T::kIsPostgresBuildInTypeWrapper),
const bool>,
92 "kIsPostgresBuildInTypeWrapper must be bool"
97concept IsPostgresBuildInTypeWrapper = IsPostgresBuildInTypeWrapperImpl<T>::value;
100constexpr bool DetectIsSuitableRowType() {
101 using type = std::remove_cv_t<T>;
102 return std::is_class_v<type> && !std::is_empty_v<type> &&
103 boost::pfr::is_implicitly_reflectable_v<type, detail::ForDeserializationTag> &&
104 !std::is_polymorphic_v<type> && !std::is_union_v<type> && !
postgres::detail::IsInStdNamespace<type> &&
105 !
postgres::detail::IsInBoostNamespace<type> && !detail::IsPostgresBuildInTypeWrapper<type>;
109struct IsSuitableRowType : BoolConstant<detail::DetectIsSuitableRowType<T>()> {};
111template <
typename Tag,
typename T, USERVER_NAMESPACE::
utils::StrongTypedefOps Ops>
112struct IsSuitableRowType<USERVER_NAMESPACE::
utils::StrongTypedef<Tag, T, Ops>> : IsSuitableRowType<T> {};
116enum class RowCategoryType { kNonRow, kTuple, kAggregate, kIntrusiveIntrospection };
118template <RowCategoryType Tag>
119using RowCategoryConstant = std::integral_constant<RowCategoryType, Tag>;
123 : std::conditional_t<
125 RowCategoryConstant<RowCategoryType::kTuple>,
128 RowCategoryConstant<RowCategoryType::kIntrusiveIntrospection>,
130 detail::IsSuitableRowType<T>::value,
131 RowCategoryConstant<RowCategoryType::kAggregate>,
132 RowCategoryConstant<RowCategoryType::kNonRow>>>> {};
134template <
typename Tag,
typename T, USERVER_NAMESPACE::
utils::StrongTypedefOps Ops>
138inline constexpr RowCategoryType kRowCategory = RowCategory<T>::value;
141constexpr void AssertIsValidRowType() {
143 kRowCategory<T> != RowCategoryType::kNonRow,
144 "Row type must be one of the following: "
145 "1. primitive type. "
147 "3. Aggregation type. See std::aggregation. "
148 "4. Has a Introspect method that makes the std::tuple from your class/struct. "
149 "For more info see `uPg: Typed PostgreSQL results` chapter in docs."
154concept IsRowType = kRowCategory<T> != RowCategoryType::kNonRow;
193template <
typename T,
traits::RowCategoryType C>
196 traits::kRowCategory<T> !=
traits::RowCategoryType::kNonRow,
197 "This type cannot be used as a row type"
202struct RowTypeImpl<T,
traits::RowCategoryType::kTuple> {
205 static constexpr std::size_t size = std::tuple_size<TupleType>::value;
206 using IndexSequence = std::make_index_sequence<size>;
208 static TupleType& GetTuple(ValueType& v) {
return v; }
209 static const TupleType& GetTuple(
const ValueType& v) {
return v; }
213struct RowTypeImpl<T,
traits::RowCategoryType::kAggregate> {
215 using TupleType =
decltype(boost::pfr::structure_tie(std::declval<ValueType&>()));
216 static constexpr std::size_t size = std::tuple_size<TupleType>::value;
218 using IndexSequence = std::make_index_sequence<size>;
219 static TupleType GetTuple(ValueType& v) {
return boost::pfr::structure_tie(v); }
220 static auto GetTuple(
const ValueType& value) {
return boost::pfr::structure_to_tuple(value); }
224struct RowTypeImpl<T,
traits::RowCategoryType::kIntrusiveIntrospection> {
226 using TupleType =
decltype(std::declval<ValueType&>().Introspect());
227 static constexpr std::size_t size = std::tuple_size<TupleType>::value;
228 using IndexSequence = std::make_index_sequence<size>;
231 static TupleType GetTuple(ValueType& v) {
return v.Introspect(); }
232 static auto GetTuple(
const ValueType& v) {
236 return ConstRefTuple{
const_cast<ValueType&>(v).Introspect()};