userver: userver/storages/postgres/io/row_types.hpp Source File
Loading...
Searching...
No Matches
row_types.hpp
1#pragma once
2
3#include <boost/pfr/core.hpp>
4#include <boost/pfr/traits.hpp>
5
6#include <userver/storages/postgres/detail/is_in_namespace.hpp>
7#include <userver/storages/postgres/io/type_traits.hpp>
8
9#include <userver/utils/strong_typedef.hpp>
10
11USERVER_NAMESPACE_BEGIN
12
13namespace storages::postgres {
14
15/// @brief Tag type to disambiguate reading the row to a user's row type
16/// (values of the row initialize user's type data members).
17///
18/// @snippet storages/postgres/tests/typed_rows_pgtest.cpp RowTagSippet
19struct RowTag {};
20
21/// @brief Tag type to disambiguate reading the first value of a row to a
22/// user's composite type (PostgreSQL composite type in the row initializes
23/// user's type).
24///
25/// @snippet storages/postgres/tests/composite_types_pgtest.cpp FieldTagSippet
26struct FieldTag {};
27
28inline constexpr RowTag kRowTag{};
29inline constexpr FieldTag kFieldTag{};
30
31namespace io {
32
33namespace traits {
34//@{
35/** @name Row type traits */
36template <typename T>
37struct IsTuple : std::false_type {};
38template <typename... T>
39struct IsTuple<std::tuple<T...>> : std::true_type {};
40
41namespace impl {
42
43template <typename T, typename = USERVER_NAMESPACE::utils::void_t<>>
44struct HasConstIntrospection : std::false_type {};
45
46template <typename T>
47struct HasConstIntrospection<T, USERVER_NAMESPACE::utils::void_t<decltype(std::declval<const T&>().Introspect())>>
48 : std::true_type {};
49
50template <typename T, typename = USERVER_NAMESPACE::utils::void_t<>>
51struct HasNonConstIntrospection : std::false_type {
52 static_assert(
53 !impl::HasConstIntrospection<T>::value,
54 "PostgreSQL driver requires non-const Introspect(). "
55 "Example: auto Introspect() { return std::tie(a, b, c, d); }"
56 );
57};
58
59template <typename T>
60struct HasNonConstIntrospection<T, USERVER_NAMESPACE::utils::void_t<decltype(std::declval<T&>().Introspect())>>
61 : std::true_type {
62 static_assert(
63 IsTuple<decltype(std::declval<T&>().Introspect())>::value,
64 "Introspect() should return a std::tuple. "
65 "Example: auto Introspect() { return std::tie(a, b, c, d); }"
66 );
67};
68
69} // namespace impl
70
71template <typename T>
72struct HasIntrospection : impl::HasNonConstIntrospection<T> {
73 static_assert(!std::is_const_v<T>);
74 static_assert(!std::is_reference_v<T>);
75};
76
77namespace detail {
78
79struct ForDeserializationTag;
80
81template <typename T, typename = USERVER_NAMESPACE::utils::void_t<>>
82struct IsPostgresBuildInTypeWrapper : std::false_type {};
83
84template <typename T>
85struct IsPostgresBuildInTypeWrapper<T, USERVER_NAMESPACE::utils::void_t<decltype(T::kIsPostgresBuildInTypeWrapper)>>
86 : std::integral_constant<const bool, T::kIsPostgresBuildInTypeWrapper> {
87 static_assert(
88 std::is_same_v<decltype(T::kIsPostgresBuildInTypeWrapper), const bool>,
89 "kIsPostgresBuildInTypeWrapper must be bool"
90 );
91};
92
93template <typename T>
94inline constexpr bool kIsPostgresBuildInTypeWrapper = IsPostgresBuildInTypeWrapper<T>::value;
95
96template <typename T>
97constexpr bool DetectIsSuitableRowType() {
98 using type = std::remove_cv_t<T>;
99 return std::is_class_v<type> && !std::is_empty_v<type> &&
100 boost::pfr::is_implicitly_reflectable_v<type, detail::ForDeserializationTag> &&
101 !std::is_polymorphic_v<type> && !std::is_union_v<type> && !postgres::detail::kIsInStdNamespace<type> &&
102 !postgres::detail::kIsInBoostNamespace<type> && !detail::kIsPostgresBuildInTypeWrapper<type>;
103}
104
105} // namespace detail
106
107template <typename T>
108struct IsSuitableRowType : BoolConstant<detail::DetectIsSuitableRowType<T>()> {};
109
110template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
111struct IsSuitableRowType<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>> : IsSuitableRowType<T> {};
112
113template <typename T>
114inline constexpr bool kIsSuitableRowType = IsSuitableRowType<T>::value;
115
116enum class RowCategoryType { kNonRow, kTuple, kAggregate, kIntrusiveIntrospection };
117
118template <RowCategoryType Tag>
119using RowCategoryConstant = std::integral_constant<RowCategoryType, Tag>;
120
121template <typename T>
123 : std::conditional_t<
124 IsTuple<T>::value,
125 RowCategoryConstant<RowCategoryType::kTuple>,
126 std::conditional_t<
127 HasIntrospection<T>::value,
128 RowCategoryConstant<RowCategoryType::kIntrusiveIntrospection>,
129 std::conditional_t<
130 IsSuitableRowType<T>::value,
131 RowCategoryConstant<RowCategoryType::kAggregate>,
132 RowCategoryConstant<RowCategoryType::kNonRow>>>> {};
133
134template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
135struct RowCategory<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>> : RowCategory<T> {};
136
137template <typename T>
138inline constexpr RowCategoryType kRowCategory = RowCategory<T>::value;
139
140template <typename T>
141constexpr void AssertIsValidRowType() {
142 static_assert(
143 kRowCategory<T> != RowCategoryType::kNonRow,
144 "Row type must be one of the following: "
145 "1. primitive type. "
146 "2. std::tuple. "
147 "3. Aggregation type. See std::aggregation. "
148 "4. Has a Introspect method that makes the std::tuple from your "
149 "class/struct. "
150 "For more info see `uPg: Typed PostgreSQL results` chapter in docs."
151 );
152}
153
154template <typename T>
155inline constexpr bool kIsRowType = kRowCategory<T> != RowCategoryType::kNonRow;
156
157template <typename T>
158inline constexpr bool kIsCompositeType = kIsRowType<T>;
159
160template <typename T>
161inline constexpr bool kIsColumnType = kRowCategory<T> == RowCategoryType::kNonRow;
162
163template <typename T, typename Enable = USERVER_NAMESPACE::utils::void_t<>>
165 using type = FieldTag;
166};
167
168template <typename T>
169struct ExtractionTag<T, std::enable_if_t<kIsRowType<T>>> {
170 using type = RowTag;
171};
172
173template <typename T>
174inline constexpr typename ExtractionTag<T>::type kExtractionTag{};
175
176} // namespace traits
177
178namespace detail {
179
180template <typename T, traits::RowCategoryType C>
181struct RowTypeImpl {
182 static_assert(
183 traits::kRowCategory<T> != traits::RowCategoryType::kNonRow,
184 "This type cannot be used as a row type"
185 );
186};
187
188template <typename T>
189struct RowTypeImpl<T, traits::RowCategoryType::kTuple> {
190 using ValueType = T;
191 using TupleType = T;
192 static constexpr std::size_t size = std::tuple_size<TupleType>::value;
193 using IndexSequence = std::make_index_sequence<size>;
194
195 static TupleType& GetTuple(ValueType& v) { return v; }
196 static const TupleType& GetTuple(const ValueType& v) { return v; }
197};
198
199template <typename T>
200struct RowTypeImpl<T, traits::RowCategoryType::kAggregate> {
201 using ValueType = T;
202 using TupleType = decltype(boost::pfr::structure_tie(std::declval<ValueType&>()));
203 static constexpr std::size_t size = std::tuple_size<TupleType>::value;
204
205 using IndexSequence = std::make_index_sequence<size>;
206 static TupleType GetTuple(ValueType& v) { return boost::pfr::structure_tie(v); }
207 static auto GetTuple(const ValueType& value) { return boost::pfr::structure_to_tuple(value); }
208};
209
210template <typename T>
211struct RowTypeImpl<T, traits::RowCategoryType::kIntrusiveIntrospection> {
212 using ValueType = T;
213 using TupleType = decltype(std::declval<ValueType&>().Introspect());
214 static constexpr std::size_t size = std::tuple_size<TupleType>::value;
215 using IndexSequence = std::make_index_sequence<size>;
216 using ConstRefTuple = typename traits::AddTupleConstRef<TupleType>::type;
217
218 static TupleType GetTuple(ValueType& v) { return v.Introspect(); }
219 static auto GetTuple(const ValueType& v) {
220 // const_cast here is to relieve users from burden of writing
221 // const-overloaded functions or static template Introspect functions.
222 /// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
223 return ConstRefTuple{const_cast<ValueType&>(v).Introspect()};
224 }
225};
226
227} // namespace detail
228
229template <typename T>
230struct RowType : detail::RowTypeImpl<T, traits::kRowCategory<T>> {};
231
232} // namespace io
233} // namespace storages::postgres
234
235USERVER_NAMESPACE_END