userver: userver/storages/postgres/io/composite_types.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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