userver: userver/storages/postgres/io/composite_types.hpp Source File
Loading...
Searching...
No Matches
composite_types.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/postgres/io/composite_types.hpp
4/// @brief Composite types I/O support
5/// @ingroup userver_postgres_parse_and_format
6
7#include <boost/pfr/core.hpp>
8#include <utility>
9
10#include <userver/compiler/demangle.hpp>
11
12#include <userver/storages/postgres/exceptions.hpp>
13#include <userver/storages/postgres/io/buffer_io_base.hpp>
14#include <userver/storages/postgres/io/field_buffer.hpp>
15#include <userver/storages/postgres/io/row_types.hpp>
16#include <userver/storages/postgres/io/traits.hpp>
17#include <userver/storages/postgres/io/type_mapping.hpp>
18#include <userver/storages/postgres/io/type_traits.hpp>
19#include <userver/storages/postgres/io/user_types.hpp>
20
21USERVER_NAMESPACE_BEGIN
22
23namespace storages::postgres::io {
24
25namespace detail {
26
27void InitRecordParser();
28
29template <typename T>
30struct CompositeBinaryParser : BufferParserBase<T> {
31 using BaseType = BufferParserBase<T>;
32 using RowType = io::RowType<T>;
33 using IndexSequence = typename RowType::IndexSequence;
34
35 using BaseType::BaseType;
36
37 void operator()(FieldBuffer buffer, const TypeBufferCategory& categories) {
38 if constexpr (!traits::kIsMappedToUserType<T>) {
39 InitRecordParser();
40 }
41
42 Integer field_count{0};
43 buffer.Read(field_count, BufferCategory::kPlainBuffer);
44
45 if (field_count != RowType::size) {
46 throw CompositeSizeMismatch(field_count, RowType::size, compiler::GetTypeName<T>());
47 }
48
49 ReadTuple(buffer, categories, RowType::GetTuple(this->value), IndexSequence{});
50 }
51
52private:
53 static constexpr std::size_t int_size = sizeof(Integer);
54
55 template <typename U>
56 void ReadField(FieldBuffer& buffer, const TypeBufferCategory& categories, U& val) const {
57 Integer field_type = 0;
58 buffer.Read(field_type, BufferCategory::kPlainBuffer);
59 auto elem_category = GetTypeBufferCategory(categories, field_type);
60 if (elem_category == BufferCategory::kNoParser) {
62 static_cast<Oid>(field_type), compiler::GetTypeName<U>(), compiler::GetTypeName<T>()};
63 }
64 buffer.ReadRaw(val, categories, elem_category);
65 }
66
67 template <typename Tuple, std::size_t... Indexes>
68 void
69 ReadTuple(FieldBuffer& buffer, const TypeBufferCategory& categories, Tuple&& tuple, std::index_sequence<Indexes...>)
70 const {
71 (ReadField(buffer, categories, std::get<Indexes>(std::forward<Tuple>(tuple))), ...);
72 }
73};
74
75template <typename T>
76struct CompositeBinaryFormatter : BufferFormatterBase<T> {
77 using BaseType = BufferFormatterBase<T>;
78 using PgMapping = CppToPg<T>;
79 using RowType = io::RowType<T>;
80 using IndexSequence = typename RowType::IndexSequence;
81 static constexpr std::size_t size = RowType::size;
82
83 using BaseType::BaseType;
84
85 template <typename Buffer>
86 void operator()(const UserTypes& types, Buffer& buffer) const {
87 const auto oid = PgMapping::GetOid(types);
88 if (!oid) {
89 auto msg = "Type '" + kPgUserTypeName<T>.ToString() +
90 "' was not created in database and because of that the '" + compiler::GetTypeName<T>() +
91 "' could not be serialized. Forgot a migration or rolled it "
92 "after the service started?";
93 throw UserTypeError(std::move(msg));
94 }
95
96 const auto& type_desc = types.GetCompositeDescription(oid);
97 if (type_desc.Size() != size) {
98 throw CompositeSizeMismatch{type_desc.Size(), size, compiler::GetTypeName<T>()};
99 }
100 // Number of fields
101 io::WriteBuffer(types, buffer, static_cast<Integer>(size));
102 WriteTuple(types, type_desc, buffer, RowType::GetTuple(this->value), IndexSequence{});
103 }
104
105private:
106 template <typename Buffer, typename U>
107 void WriteField(
108 const UserTypes& types,
109 const CompositeTypeDescription& type_desc,
110 std::size_t index,
111 Buffer& buffer,
112 const U& val
113 ) const {
114 auto field_type = CppToPg<U>::GetOid(types);
115 const auto& field_desc = type_desc[index];
116 if (field_type != field_desc.type) {
117 if (io::MappedToSameType(
118 static_cast<PredefinedOids>(field_type),
119 static_cast<PredefinedOids>(types.FindDomainBaseOid(field_desc.type))
120 )) {
121 field_type = field_desc.type;
122 } else {
123 throw CompositeMemberTypeMismatch(
124 PgMapping::postgres_name.schema,
125 PgMapping::postgres_name.name,
126 field_desc.name,
127 field_desc.type,
128 field_type
129 );
130 }
131 }
132 io::WriteBuffer(types, buffer, static_cast<Integer>(field_type));
133 io::WriteRawBinary(types, buffer, val, field_type);
134 }
135 template <typename Buffer, typename Tuple, std::size_t... Indexes>
136 void
137 WriteTuple(const UserTypes& types, const CompositeTypeDescription& type_desc, Buffer& buffer, Tuple&& tuple, std::index_sequence<Indexes...>)
138 const {
139 (WriteField(types, type_desc, Indexes, buffer, std::get<Indexes>(std::forward<Tuple>(tuple))), ...);
140 }
141};
142
143} // namespace detail
144
145namespace traits {
146
147namespace detail {
148
149template <typename Tuple>
150struct AssertTupleHasParsers;
151
152template <typename... Members>
153struct AssertTupleHasParsers<std::tuple<Members...>> : std::true_type {
154 static_assert(
155 (HasParser<std::decay_t<Members>>::value && ...),
156 "No parser for member. Probably you forgot to include "
157 "file with parser or to define your own. Please see page "
158 "`uPg: Supported data types` for more information"
159 );
160};
161
162template <typename Tuple>
163struct AssertTupleHasFormatters;
164
165template <typename... Members>
166struct AssertTupleHasFormatters<std::tuple<Members...>> : std::true_type {
167 static_assert(
168 (HasFormatter<std::decay_t<Members>>::value && ...),
169 "No formatter for member. Probably you forgot to "
170 "include file with formatter or to define your own. Please see page "
171 "`uPg: Supported data types` for more information"
172 );
173};
174
175template <typename T>
176constexpr bool AssertHasCompositeParsers() {
177 io::traits::AssertIsValidRowType<T>();
178 return AssertTupleHasParsers<typename io::RowType<T>::TupleType>::value;
179}
180
181template <typename T>
182constexpr bool AssertHasCompositeFormatters() {
183 io::traits::AssertIsValidRowType<T>();
184 return AssertTupleHasFormatters<typename io::RowType<T>::TupleType>::value;
185}
186
187} // namespace detail
188
189template <typename T>
191 static_assert(detail::AssertHasCompositeParsers<T>());
193};
194
195template <typename T>
197 static_assert(detail::AssertHasCompositeFormatters<T>());
199};
200
201template <typename T>
202struct ParserBufferCategory<io::detail::CompositeBinaryParser<T>>
204
205} // namespace traits
206} // namespace storages::postgres::io
207
208USERVER_NAMESPACE_END