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