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<
48 T, USERVER_NAMESPACE::utils::void_t<
49 decltype(std::declval<const T&>().Introspect())>> : std::true_type {
50};
51
52template <typename T, typename = USERVER_NAMESPACE::utils::void_t<>>
53struct HasNonConstIntrospection : std::false_type {
54 static_assert(!impl::HasConstIntrospection<T>::value,
55 "PostgreSQL driver requires non-const Introspect(). "
56 "Example: auto Introspect() { return std::tie(a, b, c, d); }");
57};
58
59template <typename T>
60struct HasNonConstIntrospection<
61 T,
62 USERVER_NAMESPACE::utils::void_t<decltype(std::declval<T&>().Introspect())>>
63 : std::true_type {
64 static_assert(IsTuple<decltype(std::declval<T&>().Introspect())>::value,
65 "Introspect() should return a std::tuple. "
66 "Example: auto Introspect() { return std::tie(a, b, c, d); }");
67};
68
69} // namespace impl
70
71template <typename 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>
82constexpr bool DetectIsSuitableRowType() {
83 using type = std::remove_cv_t<T>;
84 return std::is_class_v<type> && !std::is_empty_v<type> &&
85 boost::pfr::is_implicitly_reflectable_v<
86 type, detail::ForDeserializationTag> &&
87 !std::is_polymorphic_v<type> && !std::is_union_v<type> &&
88 !postgres::detail::kIsInStdNamespace<type> &&
89 !postgres::detail::kIsInBoostNamespace<type>;
90}
91
92} // namespace detail
93
94template <typename T>
96};
97
98template <typename Tag, typename T,
99 USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
100struct IsSuitableRowType<
101 USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>>
102 : IsSuitableRowType<T> {};
103
104template <typename T>
105inline constexpr bool kIsSuitableRowType = IsSuitableRowType<T>::value;
106
107enum class RowCategoryType {
108 kNonRow,
109 kTuple,
110 kAggregate,
111 kIntrusiveIntrospection
112};
113
114template <RowCategoryType Tag>
116
117template <typename T>
118struct RowCategory
128
129template <typename Tag, typename T,
130 USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
131struct RowCategory<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>>
132 : RowCategory<T> {};
133
134template <typename T>
135inline constexpr RowCategoryType kRowCategory = RowCategory<T>::value;
136
137template <typename T>
138constexpr void AssertIsValidRowType() {
139 static_assert(
140 kRowCategory<T> != RowCategoryType::kNonRow,
141 "Row type must be one of the following: "
142 "1. primitive type. "
143 "2. std::tuple. "
144 "3. Aggregation type. See std::aggregation. "
145 "4. Has a Introspect method that makes the std::tuple from your "
146 "class/struct. "
147 "For more info see `uPg: Typed PostgreSQL results` chapter in docs.");
148}
149
150template <typename T>
151inline constexpr bool kIsRowType = kRowCategory<T> != RowCategoryType::kNonRow;
152
153template <typename T>
154inline constexpr bool kIsCompositeType = kIsRowType<T>;
155
156template <typename T>
157inline constexpr bool kIsColumnType =
158 kRowCategory<T> == RowCategoryType::kNonRow;
159
160template <typename T, typename Enable = USERVER_NAMESPACE::utils::void_t<>>
161struct ExtractionTag {
162 using type = FieldTag;
163};
164
165template <typename T>
167 using type = RowTag;
168};
169
170template <typename T>
171inline constexpr typename ExtractionTag<T>::type kExtractionTag{};
172
173} // namespace traits
174
175namespace detail {
176
177template <typename T, traits::RowCategoryType C>
178struct RowTypeImpl {
179 static_assert(traits::kRowCategory<T> != traits::RowCategoryType::kNonRow,
180 "This type cannot be used as a row type");
181};
182
183template <typename T>
184struct RowTypeImpl<T, traits::RowCategoryType::kTuple> {
185 using ValueType = T;
186 using TupleType = T;
187 static constexpr std::size_t size = std::tuple_size<TupleType>::value;
188 using IndexSequence = std::make_index_sequence<size>;
189
190 static TupleType& GetTuple(ValueType& v) { return v; }
191 static const TupleType& GetTuple(const ValueType& v) { return v; }
192};
193
194template <typename T>
195struct RowTypeImpl<T, traits::RowCategoryType::kAggregate> {
196 using ValueType = T;
197 using TupleType =
198 decltype(boost::pfr::structure_tie(std::declval<ValueType&>()));
199 static constexpr std::size_t size = std::tuple_size<TupleType>::value;
200
201 using IndexSequence = std::make_index_sequence<size>;
202 static TupleType GetTuple(ValueType& v) {
203 return boost::pfr::structure_tie(v);
204 }
205 static auto GetTuple(const ValueType& value) {
206 return boost::pfr::structure_to_tuple(value);
207 }
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