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
11// TODO remove extra include.
12#include <userver/utils/text_light.hpp>
13
14USERVER_NAMESPACE_BEGIN
15
16namespace storages::postgres {
17
18/// @brief Tag type to disambiguate reading the row to a user's row type
19/// (values of the row initialize user's type data members).
20///
21/// @snippet storages/postgres/tests/typed_rows_pgtest.cpp RowTagSippet
22struct RowTag {};
23
24/// @brief Tag type to disambiguate reading the first value of a row to a
25/// user's composite type (PostgreSQL composite type in the row initializes
26/// user's type).
27///
28/// @snippet storages/postgres/tests/composite_types_pgtest.cpp FieldTagSippet
29struct FieldTag {};
30
31inline constexpr RowTag kRowTag{};
32inline constexpr FieldTag kFieldTag{};
33
34namespace io {
35
36namespace traits {
37//@{
38/** @name Row type traits */
39template <typename T>
40struct IsTuple : std::false_type {};
41template <typename... T>
42struct IsTuple<std::tuple<T...>> : std::true_type {};
43
44namespace impl {
45
46template <typename T>
47struct HasConstIntrospection : std::false_type {};
48
49template <typename T>
50requires requires(const T& t) { t.Introspect(); }
51struct HasConstIntrospection<T> : std::true_type {};
52
53template <typename T>
54struct HasNonConstIntrospection : std::false_type {
55 static_assert(
56 !impl::HasConstIntrospection<T>::value,
57 "PostgreSQL driver requires non-const Introspect(). "
58 "Example: auto Introspect() { return std::tie(a, b, c, d); }"
59 );
60};
61
62template <typename T>
63requires requires(T& t) { t.Introspect(); }
64struct HasNonConstIntrospection<T> : std::true_type {
65 static_assert(
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); }"
69 );
70};
71
72} // namespace impl
73
74template <typename T>
75struct HasIntrospection : impl::HasNonConstIntrospection<T> {
76 static_assert(!std::is_const_v<T>);
77 static_assert(!std::is_reference_v<T>);
78};
79
80namespace detail {
81
82struct ForDeserializationTag;
83
84template <typename T>
85struct IsPostgresBuildInTypeWrapperImpl : std::false_type {};
86
87template <typename T>
88requires requires { T::kIsPostgresBuildInTypeWrapper; }
89struct IsPostgresBuildInTypeWrapperImpl<T> : std::integral_constant<const bool, T::kIsPostgresBuildInTypeWrapper> {
90 static_assert(
91 std::is_same_v<decltype(T::kIsPostgresBuildInTypeWrapper), const bool>,
92 "kIsPostgresBuildInTypeWrapper must be bool"
93 );
94};
95
96template <typename T>
97concept IsPostgresBuildInTypeWrapper = IsPostgresBuildInTypeWrapperImpl<T>::value;
98
99template <typename T>
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>;
106}
107
108template <typename T>
109struct IsSuitableRowType : BoolConstant<detail::DetectIsSuitableRowType<T>()> {};
110
111template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops>
112struct IsSuitableRowType<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>> : IsSuitableRowType<T> {};
113
114} // namespace detail
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 detail::IsSuitableRowType<T>::value,
131 RowCategoryConstant<RowCategoryType::kAggregate>,
132 RowCategoryConstant<RowCategoryType::kNonRow>>>> {};
133
134template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops>
135struct RowCategory<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>> : 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 class/struct. "
149 "For more info see `uPg: Typed PostgreSQL results` chapter in docs."
150 );
151}
152
153template <typename T>
154concept IsRowType = kRowCategory<T> != RowCategoryType::kNonRow;
155
156template <typename T>
158
159template <typename T>
160concept IsColumnType = kRowCategory<T> == RowCategoryType::kNonRow;
161
162// NOLINTBEGIN(readability-identifier-naming)
163
164/// @deprecated
165template <typename T>
167
168/// @deprecated
169template <typename T>
171
172/// @deprecated
173template <typename T>
175
176// NOLINTEND(readability-identifier-naming)
177
178template <typename T>
180 using type = FieldTag;
181};
182
183template <typename T>
184requires IsRowType<T>
185struct ExtractionTag<T> {
186 using type = RowTag;
187};
188
189} // namespace traits
190
191namespace detail {
192
193template <typename T, traits::RowCategoryType C>
194struct RowTypeImpl {
195 static_assert(
196 traits::kRowCategory<T> != traits::RowCategoryType::kNonRow,
197 "This type cannot be used as a row type"
198 );
199};
200
201template <typename T>
202struct RowTypeImpl<T, traits::RowCategoryType::kTuple> {
203 using ValueType = T;
204 using TupleType = T;
205 static constexpr std::size_t size = std::tuple_size<TupleType>::value;
206 using IndexSequence = std::make_index_sequence<size>;
207
208 static TupleType& GetTuple(ValueType& v) { return v; }
209 static const TupleType& GetTuple(const ValueType& v) { return v; }
210};
211
212template <typename T>
213struct RowTypeImpl<T, traits::RowCategoryType::kAggregate> {
214 using ValueType = T;
215 using TupleType = decltype(boost::pfr::structure_tie(std::declval<ValueType&>()));
216 static constexpr std::size_t size = std::tuple_size<TupleType>::value;
217
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); }
221};
222
223template <typename T>
224struct RowTypeImpl<T, traits::RowCategoryType::kIntrusiveIntrospection> {
225 using ValueType = T;
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>;
229 using ConstRefTuple = typename traits::AddTupleConstRef<TupleType>::type;
230
231 static TupleType GetTuple(ValueType& v) { return v.Introspect(); }
232 static auto GetTuple(const ValueType& v) {
233 // const_cast here is to relieve users from burden of writing
234 // const-overloaded functions or static template Introspect functions.
235 /// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
236 return ConstRefTuple{const_cast<ValueType&>(v).Introspect()};
237 }
238};
239
240} // namespace detail
241
242template <typename T>
243struct RowType : detail::RowTypeImpl<T, traits::kRowCategory<T>> {};
244
245} // namespace io
246} // namespace storages::postgres
247
248USERVER_NAMESPACE_END