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,
47 compiler::GetTypeName<T>());
48 }
49
50 ReadTuple(buffer, categories, RowType::GetTuple(this->value),
51 IndexSequence{});
52 }
53
54 private:
55 static constexpr std::size_t int_size = sizeof(Integer);
56
57 template <typename U>
58 void ReadField(FieldBuffer& buffer, const TypeBufferCategory& categories,
59 U& val) const {
60 Integer field_type = 0;
61 buffer.Read(field_type, BufferCategory::kPlainBuffer);
62 auto elem_category = GetTypeBufferCategory(categories, field_type);
63 if (elem_category == BufferCategory::kNoParser) {
64 throw LogicError{"Buffer category for oid " + std::to_string(field_type) +
65 " is unknown"};
66 }
67 buffer.ReadRaw(val, categories, elem_category);
68 }
69 template <typename Tuple, std::size_t... Indexes>
70 void ReadTuple(FieldBuffer& buffer, const TypeBufferCategory& categories,
71 Tuple&& tuple, std::index_sequence<Indexes...>) const {
72 (ReadField(buffer, categories,
73 std::get<Indexes>(std::forward<Tuple>(tuple))),
74 ...);
75 }
76};
77
78template <typename T>
79struct CompositeBinaryFormatter : BufferFormatterBase<T> {
80 using BaseType = BufferFormatterBase<T>;
81 using PgMapping = CppToPg<T>;
82 using RowType = io::RowType<T>;
83 using IndexSequence = typename RowType::IndexSequence;
84 static constexpr std::size_t size = RowType::size;
85
86 using BaseType::BaseType;
87
88 template <typename Buffer>
89 void operator()(const UserTypes& types, Buffer& buffer) const {
90 const auto& type_desc =
91 types.GetCompositeDescription(PgMapping::GetOid(types));
92 if (type_desc.Size() != size) {
93 throw CompositeSizeMismatch{type_desc.Size(), size,
94 compiler::GetTypeName<T>()};
95 }
96 // Number of fields
97 io::WriteBuffer(types, buffer, static_cast<Integer>(size));
98 WriteTuple(types, type_desc, buffer, RowType::GetTuple(this->value),
99 IndexSequence{});
100 }
101
102 private:
103 template <typename Buffer, typename U>
104 void WriteField(const UserTypes& types,
105 const CompositeTypeDescription& type_desc, std::size_t index,
106 Buffer& buffer, const U& val) const {
107 auto field_type = CppToPg<U>::GetOid(types);
108 const auto& field_desc = type_desc[index];
109 if (field_type != field_desc.type) {
110 if (io::MappedToSameType(static_cast<PredefinedOids>(field_type),
111 static_cast<PredefinedOids>(
112 types.FindDomainBaseOid(field_desc.type)))) {
113 field_type = field_desc.type;
114 } else {
115 throw CompositeMemberTypeMismatch(
116 PgMapping::postgres_name.schema, PgMapping::postgres_name.name,
117 field_desc.name, field_desc.type, field_type);
118 }
119 }
120 io::WriteBuffer(types, buffer, static_cast<Integer>(field_type));
121 io::WriteRawBinary(types, buffer, val, field_type);
122 }
123 template <typename Buffer, typename Tuple, std::size_t... Indexes>
124 void WriteTuple(const UserTypes& types,
125 const CompositeTypeDescription& type_desc, Buffer& buffer,
126 Tuple&& tuple, std::index_sequence<Indexes...>) const {
127 (WriteField(types, type_desc, Indexes, buffer,
128 std::get<Indexes>(std::forward<Tuple>(tuple))),
129 ...);
130 }
131};
132
133} // namespace detail
134
135namespace traits {
136
137namespace detail {
138
139template <typename Tuple>
140struct AssertTupleHasParsers;
141
142template <typename... Members>
143struct AssertTupleHasParsers<std::tuple<Members...>> : std::true_type {
144 static_assert((HasParser<std::decay_t<Members>>::value && ...),
145 "No parser for member. Probably you forgot to include "
146 "file with parser or to define your own. Please see page "
147 "`uPg: Supported data types` for more information");
148};
149
150template <typename Tuple>
151struct AssertTupleHasFormatters;
152
153template <typename... Members>
154struct AssertTupleHasFormatters<std::tuple<Members...>> : std::true_type {
155 static_assert(
156 (HasFormatter<std::decay_t<Members>>::value && ...),
157 "No formatter for member. Probably you forgot to "
158 "include file with formatter or to define your own. Please see page "
159 "`uPg: Supported data types` for more information");
160};
161
162template <typename T>
163constexpr bool AssertHasCompositeParsers() {
164 io::traits::AssertIsValidRowType<T>();
165 return AssertTupleHasParsers<typename io::RowType<T>::TupleType>::value;
166}
167
168template <typename T>
169constexpr bool AssertHasCompositeFormatters() {
170 io::traits::AssertIsValidRowType<T>();
171 return AssertTupleHasFormatters<typename io::RowType<T>::TupleType>::value;
172}
173
174} // namespace detail
175
176template <typename T>
177struct Input<
179 static_assert(detail::AssertHasCompositeParsers<T>());
181};
182
183template <typename T>
186 static_assert(detail::AssertHasCompositeFormatters<T>());
188};
189
190template <typename T>
191struct ParserBufferCategory<io::detail::CompositeBinaryParser<T>>
193};
194
195} // namespace traits
196} // namespace storages::postgres::io
197
198USERVER_NAMESPACE_END