8#include <boost/pfr/core.hpp>
11#include <userver/compiler/demangle.hpp>
13#include <userver/storages/postgres/exceptions.hpp>
14#include <userver/storages/postgres/io/buffer_io_base.hpp>
15#include <userver/storages/postgres/io/field_buffer.hpp>
16#include <userver/storages/postgres/io/row_types.hpp>
17#include <userver/storages/postgres/io/traits.hpp>
18#include <userver/storages/postgres/io/type_mapping.hpp>
19#include <userver/storages/postgres/io/type_traits.hpp>
20#include <userver/storages/postgres/io/user_types.hpp>
22USERVER_NAMESPACE_BEGIN
28void InitRecordParser();
31struct CompositeBinaryParser : BufferParserBase<T> {
32 using BaseType = BufferParserBase<T>;
34 using IndexSequence =
typename RowType::IndexSequence;
36 using BaseType::BaseType;
38 void operator()(
FieldBuffer buffer,
const TypeBufferCategory& categories) {
39 if constexpr (!
traits::kIsMappedToUserType<T>) {
43 Integer field_count{0};
46 if (field_count != RowType::size) {
50 ReadTuple(buffer, categories, RowType::GetTuple(
this->value), IndexSequence{});
54 static constexpr std::size_t int_size =
sizeof(Integer);
57 void ReadField(
FieldBuffer& buffer,
const TypeBufferCategory& categories, std::size_t field_index, U& val)
const {
58 Integer field_type = 0;
60 auto elem_category = GetTypeBufferCategory(categories, field_type);
63 static_cast<Oid>(field_type),
69 buffer.ReadRaw(val, categories, elem_category);
71 error.AddMsgSuffix(fmt::format(
72 ",\n\twhile reading from database to C++ type '{}' (field #{} of a composite C++ type '{}')",
81 template <
typename Tuple, std::size_t... Indexes>
83 ReadTuple(
FieldBuffer& buffer,
const TypeBufferCategory& categories, Tuple&& tuple, std::index_sequence<Indexes...>)
85 (ReadField(buffer, categories, Indexes, std::get<Indexes>(std::forward<Tuple>(tuple))), ...);
90struct CompositeBinaryFormatter : BufferFormatterBase<T> {
91 using BaseType = BufferFormatterBase<T>;
92 using PgMapping = CppToPg<T>;
94 using IndexSequence =
typename RowType::IndexSequence;
95 static constexpr std::size_t size = RowType::size;
97 using BaseType::BaseType;
99 template <
typename Buffer>
100 void operator()(
const UserTypes& types, Buffer& buffer)
const {
101 const auto oid = PgMapping::GetOid(types);
103 auto msg = fmt::format(
104 "Type '{}' was not created in database and because of that the '{}' could not be serialized. Forgot a "
105 "migration or rolled it after the service started?",
106 kPgUserTypeName<T>.ToString(),
112 const auto& type_desc = types.GetCompositeDescription(oid);
113 if (type_desc.Size() != size) {
117 io::WriteBuffer(types, buffer,
static_cast<Integer>(size));
118 WriteTuple(types, type_desc, buffer, RowType::GetTuple(
this->value), IndexSequence{});
122 template <
typename Buffer,
typename U>
130 auto field_type = CppToPg<U>::GetOid(types);
131 const auto& field_desc = type_desc[index];
132 if (field_type != field_desc.type) {
138 field_type = field_desc.type;
141 PgMapping::postgres_name.schema,
142 PgMapping::postgres_name.name,
149 io::WriteBuffer(types, buffer,
static_cast<Integer>(field_type));
150 io::WriteRawBinary(types, buffer, val, field_type);
152 template <
typename Buffer,
typename Tuple, std::size_t... Indexes>
156 (WriteField(types, type_desc, Indexes, buffer, std::get<Indexes>(std::forward<Tuple>(tuple))), ...);
166template <
typename Tuple>
167struct AssertTupleHasParsers;
169template <
typename... Members>
170struct AssertTupleHasParsers<std::tuple<Members...>> : std::true_type {
172 (
HasParser<std::decay_t<Members>>::value && ...),
173 "No parser for member. Probably you forgot to include "
174 "file with parser or to define your own. Please see page "
175 "`uPg: Supported data types` for more information"
179template <
typename Tuple>
180struct AssertTupleHasFormatters;
182template <
typename... Members>
183struct AssertTupleHasFormatters<std::tuple<Members...>> : std::true_type {
186 "No formatter for member. Probably you forgot to "
187 "include file with formatter or to define your own. Please see page "
188 "`uPg: Supported data types` for more information"
193constexpr bool AssertHasCompositeParsers() {
194 io::
traits::AssertIsValidRowType<T>();
195 return AssertTupleHasParsers<
typename io::
RowType<T>::TupleType>::value;
199constexpr bool AssertHasCompositeFormatters() {
200 io::
traits::AssertIsValidRowType<T>();
201 return AssertTupleHasFormatters<
typename io::
RowType<T>::TupleType>::value;
207struct Input<T, std::enable_if_t<!detail::kCustomParserDefined<T> && kIsRowType<T>>> {
208 static_assert(detail::AssertHasCompositeParsers<T>());
209 using type = io::detail::CompositeBinaryParser<T>;
213struct Output<T, std::enable_if_t<!detail::kCustomFormatterDefined<T> && kIsMappedToUserType<T> && kIsRowType<T>>> {
214 static_assert(detail::AssertHasCompositeFormatters<T>());
215 using type = io::detail::CompositeBinaryFormatter<T>;