userver: userver/storages/postgres/io/bytea.hpp Source File
Loading...
Searching...
No Matches
bytea.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/postgres/io/bytea.hpp
4/// @brief storages::postgres::Bytea I/O support
5/// @ingroup userver_postgres_parse_and_format
6
7#include <string>
8#include <string_view>
9#include <vector>
10
11#include <userver/storages/postgres/exceptions.hpp>
12#include <userver/storages/postgres/io/buffer_io.hpp>
13#include <userver/storages/postgres/io/buffer_io_base.hpp>
14#include <userver/storages/postgres/io/type_mapping.hpp>
15
16USERVER_NAMESPACE_BEGIN
17
18namespace storages::postgres {
19
20namespace io::traits {
21
22template <typename T>
23struct IsByteaCompatible : std::false_type {};
24
25template <>
26struct IsByteaCompatible<std::string> : std::true_type {};
27template <>
29template <typename... VectorArgs>
30struct IsByteaCompatible<std::vector<char, VectorArgs...>> : std::true_type {};
31template <typename... VectorArgs>
32struct IsByteaCompatible<std::vector<unsigned char, VectorArgs...>> : std::true_type {};
33
34template <typename T>
35inline constexpr bool kIsByteaCompatible = IsByteaCompatible<T>::value;
36
37template <typename T>
39
40} // namespace io::traits
41
42namespace detail {
43
44template <typename ByteContainerRef>
45struct ByteaRefWrapper {
46 static_assert(std::is_reference<ByteContainerRef>::value, "The container must be passed by reference");
47
48 using BytesType = std::decay_t<ByteContainerRef>;
49 static_assert(
50 io::traits::kIsByteaCompatible<BytesType>,
51 "This C++ type cannot be used with PostgreSQL `bytea` data type"
52 );
53
54 ByteContainerRef bytes;
55};
56
57} // namespace detail
58
59// clang-format off
60/// Helper function for reading binary data
61///
62/// Example usage:
63/// @snippet postgresql/src/storages/postgres/tests/bytea_pgtest.cpp bytea_simple
64/// @snippet postgresql/src/storages/postgres/tests/bytea_pgtest.cpp bytea_string
65/// @snippet postgresql/src/storages/postgres/tests/bytea_pgtest.cpp bytea_vector
66// clang-format on
67
68template <typename ByteContainer>
69detail::ByteaRefWrapper<const ByteContainer&> Bytea(const ByteContainer& bytes) {
70 return {bytes};
71}
72
73// clang-format off
74/// Helper function for reading binary data
75///
76/// Example usage:
77/// @snippet postgresql/src/storages/postgres/tests/bytea_pgtest.cpp bytea_simple
78/// @snippet postgresql/src/storages/postgres/tests/bytea_pgtest.cpp bytea_string
79/// @snippet postgresql/src/storages/postgres/tests/bytea_pgtest.cpp bytea_vector
80// clang-format on
81
82template <typename ByteContainer>
83detail::ByteaRefWrapper<ByteContainer&> Bytea(ByteContainer& bytes) {
84 return {bytes};
85}
86
87/// Wrapper for binary data container
88template <typename ByteContainer>
91
92 constexpr static bool kIsPostgresBuildInTypeWrapper = true;
93
94 static_assert(
96 "This C++ type cannot be used with PostgreSQL `bytea` data type"
97 );
98
99 ByteContainer bytes;
100};
101
102namespace io {
103
104template <typename ByteContainer>
105struct BufferParser<
106 postgres::detail::ByteaRefWrapper<ByteContainer>,
107 traits::EnableIfByteaCompatible<std::decay_t<ByteContainer>>>
108 : detail::BufferParserBase<postgres::detail::ByteaRefWrapper<ByteContainer>&&> {
109 using BaseType = detail::BufferParserBase<postgres::detail::ByteaRefWrapper<ByteContainer>&&>;
110 using BaseType::BaseType;
111 using ByteaType = postgres::detail::ByteaRefWrapper<ByteContainer>;
112
113 void operator()(const FieldBuffer& buffer) {
114 if constexpr (std::is_same<typename ByteaType::BytesType, std::string_view>{}) {
115 this->value.bytes = std::string_view{reinterpret_cast<const char*>(buffer.buffer), buffer.length};
116 } else {
117 this->value.bytes.resize(buffer.length);
118 std::copy(buffer.buffer, buffer.buffer + buffer.length, this->value.bytes.begin());
119 }
120 }
121};
122
123template <typename ByteContainer>
124struct BufferParser<postgres::ByteaWrapper<ByteContainer>, traits::EnableIfByteaCompatible<std::decay_t<ByteContainer>>>
125 : detail::BufferParserBase<postgres::ByteaWrapper<ByteContainer>> {
126 using BaseType = detail::BufferParserBase<postgres::ByteaWrapper<ByteContainer>>;
127 using BaseType::BaseType;
128
129 void operator()(const FieldBuffer& buffer) { ReadBuffer(buffer, Bytea(this->value.bytes)); }
130};
131
132template <typename ByteContainer>
133struct BufferFormatter<
134 postgres::detail::ByteaRefWrapper<ByteContainer>,
135 traits::EnableIfByteaCompatible<std::decay_t<ByteContainer>>>
136 : detail::BufferFormatterBase<postgres::detail::ByteaRefWrapper<ByteContainer>> {
137 using BaseType = detail::BufferFormatterBase<postgres::detail::ByteaRefWrapper<ByteContainer>>;
138 using BaseType::BaseType;
139
140 template <typename Buffer>
141 void operator()(const UserTypes&, Buffer& buf) const {
142 buf.reserve(buf.size() + this->value.bytes.size());
143 buf.insert(buf.end(), this->value.bytes.begin(), this->value.bytes.end());
144 }
145};
146
147template <typename ByteContainer>
148struct BufferFormatter<
149 postgres::ByteaWrapper<ByteContainer>,
150 traits::EnableIfByteaCompatible<std::decay_t<ByteContainer>>>
151 : detail::BufferFormatterBase<postgres::ByteaWrapper<ByteContainer>> {
152 using BaseType = detail::BufferFormatterBase<postgres::ByteaWrapper<ByteContainer>>;
153 using BaseType::BaseType;
154
155 template <typename Buffer>
156 void operator()(const UserTypes& types, Buffer& buffer) const {
157 WriteBuffer(types, buffer, Bytea(this->value.bytes));
158 }
159};
160
161template <typename ByteContainer>
162struct CppToSystemPg<postgres::detail::ByteaRefWrapper<ByteContainer>> : PredefinedOid<PredefinedOids::kBytea> {};
163template <typename ByteContainer>
164struct CppToSystemPg<postgres::ByteaWrapper<ByteContainer>> : PredefinedOid<PredefinedOids::kBytea> {};
165
166} // namespace io
167} // namespace storages::postgres
168
169USERVER_NAMESPACE_END