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>
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>
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>
120
121template <typename T>
123 IsTuple<T>::value,
132
133template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
134struct RowCategory<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>> : RowCategory<T> {};
135
136template <typename T>
137inline constexpr RowCategoryType kRowCategory = RowCategory<T>::value;
138
139template <typename T>
140constexpr void AssertIsValidRowType() {
141 static_assert(
142 kRowCategory<T> != RowCategoryType::kNonRow,
143 "Row type must be one of the following: "
144 "1. primitive type. "
145 "2. std::tuple. "
146 "3. Aggregation type. See std::aggregation. "
147 "4. Has a Introspect method that makes the std::tuple from your "
148 "class/struct. "
149 "For more info see `uPg: Typed PostgreSQL results` chapter in docs."
150 );
151}
152
153template <typename T>
154inline constexpr bool kIsRowType = kRowCategory<T> != RowCategoryType::kNonRow;
155
156template <typename T>
157inline constexpr bool kIsCompositeType = kIsRowType<T>;
158
159template <typename T>
160inline constexpr bool kIsColumnType = kRowCategory<T> == RowCategoryType::kNonRow;
161
162template <typename T, typename Enable = USERVER_NAMESPACE::utils::void_t<>>
163struct ExtractionTag {
164 using type = FieldTag;
165};
166
167template <typename T>
169 using type = RowTag;
170};
171
172template <typename T>
173inline constexpr typename ExtractionTag<T>::type kExtractionTag{};
174
175} // namespace traits
176
177namespace detail {
178
179template <typename T, traits::RowCategoryType C>
180struct RowTypeImpl {
181 static_assert(
182 traits::kRowCategory<T> != traits::RowCategoryType::kNonRow,
183 "This type cannot be used as a row type"
184 );
185};
186
187template <typename T>
188struct RowTypeImpl<T, traits::RowCategoryType::kTuple> {
189 using ValueType = T;
190 using TupleType = T;
191 static constexpr std::size_t size = std::tuple_size<TupleType>::value;
192 using IndexSequence = std::make_index_sequence<size>;
193
194 static TupleType& GetTuple(ValueType& v) { return v; }
195 static const TupleType& GetTuple(const ValueType& v) { return v; }
196};
197
198template <typename T>
199struct RowTypeImpl<T, traits::RowCategoryType::kAggregate> {
200 using ValueType = T;
201 using TupleType = decltype(boost::pfr::structure_tie(std::declval<ValueType&>()));
202 static constexpr std::size_t size = std::tuple_size<TupleType>::value;
203
204 using IndexSequence = std::make_index_sequence<size>;
205 static TupleType GetTuple(ValueType& v) { return boost::pfr::structure_tie(v); }
206 static auto GetTuple(const ValueType& value) { return boost::pfr::structure_to_tuple(value); }
207};
208
209template <typename T>
210struct RowTypeImpl<T, traits::RowCategoryType::kIntrusiveIntrospection> {
211 using ValueType = T;
212 using TupleType = decltype(std::declval<ValueType&>().Introspect());
213 static constexpr std::size_t size = std::tuple_size<TupleType>::value;
214 using IndexSequence = std::make_index_sequence<size>;
215 using ConstRefTuple = typename traits::AddTupleConstRef<TupleType>::type;
216
217 static TupleType GetTuple(ValueType& v) { return v.Introspect(); }
218 static auto GetTuple(const ValueType& v) {
219 // const_cast here is to relieve users from burden of writing
220 // const-overloaded functions or static template Introspect functions.
221 /// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
222 return ConstRefTuple{const_cast<ValueType&>(v).Introspect()};
223 }
224};
225
226} // namespace detail
227
228template <typename T>
229struct RowType : detail::RowTypeImpl<T, traits::kRowCategory<T>> {};
230
231} // namespace io
232} // namespace storages::postgres
233
234USERVER_NAMESPACE_END