userver: userver/storages/postgres/detail/query_parameters.hpp Source File
Loading...
Searching...
No Matches
query_parameters.hpp
1#pragma once
2
3#include <vector>
4
5#include <userver/storages/postgres/io/nullable_traits.hpp>
6#include <userver/storages/postgres/io/traits.hpp>
7#include <userver/storages/postgres/io/type_mapping.hpp>
8
9#include <userver/storages/postgres/io/supported_types.hpp>
10
11#include <userver/storages/postgres/null.hpp>
12
13USERVER_NAMESPACE_BEGIN
14
15namespace storages::postgres::detail {
16
17/// @brief Helper to write query parameters to buffers
18class QueryParameters {
19 public:
20 QueryParameters() = default;
21
22 template <class ParamsHolder>
23 explicit QueryParameters(ParamsHolder& ph)
24 : size_(ph.Size()),
25 types_(ph.ParamTypesBuffer()),
26 values_(ph.ParamBuffers()),
27 lengths_(ph.ParamLengthsBuffer()),
28 formats_(ph.ParamFormatsBuffer()) {}
29
30 bool Empty() const { return size_ == 0; }
31 std::size_t Size() const { return size_; }
32 const char* const* ParamBuffers() const { return values_; }
33 const Oid* ParamTypesBuffer() const { return types_; }
34 const int* ParamLengthsBuffer() const { return lengths_; }
35 const int* ParamFormatsBuffer() const { return formats_; }
36
37 std::size_t TypeHash() const;
38
39 private:
40 std::size_t size_ = 0;
41 const Oid* types_ = nullptr;
42 const char* const* values_ = nullptr;
43 const int* lengths_ = nullptr;
44 const int* formats_ = nullptr;
45};
46
47template <std::size_t ParamsCount>
48class StaticQueryParameters {
49 public:
50 StaticQueryParameters() = default;
51 StaticQueryParameters(const StaticQueryParameters&) = delete;
52 StaticQueryParameters(StaticQueryParameters&&) = delete;
53 StaticQueryParameters& operator=(const StaticQueryParameters&) = delete;
54 StaticQueryParameters& operator=(StaticQueryParameters&&) = delete;
55
56 std::size_t Size() const { return ParamsCount; }
57 const char* const* ParamBuffers() const { return param_buffers; }
58 const Oid* ParamTypesBuffer() const { return param_types; }
59 const int* ParamLengthsBuffer() const { return param_lengths; }
60 const int* ParamFormatsBuffer() const { return param_formats; }
61
62 template <typename T>
63 void Write(std::size_t index, const UserTypes& types, const T& arg) {
64 static_assert(io::traits::kIsMappedToPg<T> || std::is_enum_v<T>,
65 "Type doesn't have mapping to Postgres type.");
66 static_assert(
67 io::traits::kIsMappedToPg<T> || !std::is_enum_v<T>,
68 "Type doesn't have mapping to Postgres type. "
69 "Enums should be either streamed as their underlying value via the "
70 "`template<> struct CanUseEnumAsStrongTypedef<T>: std::true_type {};` "
71 "specialization or as a PostgreSQL datatype via the "
72 "`template<> struct CppToUserPg<T> : EnumMappingBase<R> { ... };` "
73 "specialization. "
74 "See page `uPg: Supported data types` for more information.");
75 WriteParamType(index, types, arg);
76 WriteNullable(index, types, arg, io::traits::IsNullable<T>{});
77 }
78
79 template <typename... T>
80 void Write(const UserTypes& types, const T&... args) {
81 std::size_t index = 0;
82 (Write(index++, types, args), ...);
83 }
84
85 private:
86 template <typename T>
87 void WriteParamType(std::size_t index, const UserTypes& types, const T&) {
88 // C++ to pg oid mapping
89 param_types[index] = io::CppToPg<T>::GetOid(types);
90 }
91
92 template <typename T>
93 void WriteNullable(std::size_t index, const UserTypes& types, const T& arg,
94 std::true_type) {
95 using NullDetector = io::traits::GetSetNull<T>;
96 if (NullDetector::IsNull(arg)) {
97 param_formats[index] = io::kPgBinaryDataFormat;
98 param_lengths[index] = io::kPgNullBufferSize;
99 param_buffers[index] = nullptr;
100 } else {
101 WriteNullable(index, types, arg, std::false_type{});
102 }
103 }
104
105 template <typename T>
106 void WriteNullable(std::size_t index, const UserTypes& types, const T& arg,
107 std::false_type) {
108 param_formats[index] = io::kPgBinaryDataFormat;
109 auto& buffer = parameters[index];
110 io::WriteBuffer(types, buffer, arg);
111 auto size = buffer.size();
112 param_lengths[index] = size;
113 if (size == 0) {
114 param_buffers[index] = empty_buffer;
115 } else {
116 param_buffers[index] = buffer.data();
117 }
118 }
119
120 using OidList = Oid[ParamsCount];
121 using BufferType = std::string;
122 using ParameterList = BufferType[ParamsCount];
123 using IntList = int[ParamsCount];
124
125 static constexpr const char* empty_buffer = "";
126
127 ParameterList parameters{};
128 OidList param_types{};
129 const char* param_buffers[ParamsCount]{};
130 IntList param_lengths{};
131 IntList param_formats{};
132};
133
134template <>
135class StaticQueryParameters<0> {
136 public:
137 static std::size_t Size() { return 0; }
138 static const char* const* ParamBuffers() { return nullptr; }
139 static const Oid* ParamTypesBuffer() { return nullptr; }
140 static const int* ParamLengthsBuffer() { return nullptr; }
141 static const int* ParamFormatsBuffer() { return nullptr; }
142
143 static void Write(const UserTypes& /*types*/) {}
144};
145
146class DynamicQueryParameters {
147 public:
148 DynamicQueryParameters() = default;
149 DynamicQueryParameters(const DynamicQueryParameters&) = delete;
150 DynamicQueryParameters(DynamicQueryParameters&&) = default;
151 DynamicQueryParameters& operator=(const DynamicQueryParameters&) = delete;
152 DynamicQueryParameters& operator=(DynamicQueryParameters&&) = default;
153
154 std::size_t Size() const { return param_types.size(); }
155 const char* const* ParamBuffers() const { return param_buffers.data(); }
156 const Oid* ParamTypesBuffer() const { return param_types.data(); }
157 const int* ParamLengthsBuffer() const { return param_lengths.data(); }
158 const int* ParamFormatsBuffer() const { return param_formats.data(); }
159
160 template <typename T>
161 void Write(const UserTypes& types, const T& arg) {
162 static_assert(io::traits::kIsMappedToPg<T>,
163 "Type doesn't have mapping to Postgres type");
164 WriteParamType(types, arg);
165 WriteNullable(types, arg, io::traits::IsNullable<T>{});
166 }
167
168 template <typename... T>
169 void Write(const UserTypes& types, const T&... args) {
170 (Write(types, args), ...);
171 }
172
173 private:
174 template <typename T>
175 void WriteParamType(const UserTypes& types, const T&) {
176 // C++ to pg oid mapping
177 param_types.push_back(io::CppToPg<T>::GetOid(types));
178 }
179
180 template <typename T>
181 void WriteNullable(const UserTypes& types, const T& arg, std::true_type) {
182 using NullDetector = io::traits::GetSetNull<T>;
183 if (NullDetector::IsNull(arg)) {
184 param_formats.push_back(io::kPgBinaryDataFormat);
185 param_lengths.push_back(io::kPgNullBufferSize);
186 param_buffers.push_back(nullptr);
187 } else {
188 WriteNullable(types, arg, std::false_type{});
189 }
190 }
191
192 template <typename T>
193 void WriteNullable(const UserTypes& types, const T& arg, std::false_type) {
194 param_formats.push_back(io::kPgBinaryDataFormat);
195 auto& buffer = parameters.emplace_back();
196 io::WriteBuffer(types, buffer, arg);
197 auto size = buffer.size();
198 param_lengths.push_back(size);
199 if (size == 0) {
200 param_buffers.push_back(empty_buffer);
201 } else {
202 param_buffers.push_back(buffer.data());
203 }
204 }
205
206 using OidList = std::vector<Oid>;
207 using BufferType = std::vector<char>;
208 using ParameterList = std::vector<BufferType>;
209 using IntList = std::vector<int>;
210
211 static constexpr const char* empty_buffer = "";
212
213 ParameterList parameters; // TODO Replace with a single buffer
214 OidList param_types;
215 std::vector<const char*> param_buffers;
216 IntList param_lengths;
217 IntList param_formats;
218};
219
220} // namespace storages::postgres::detail
221
222USERVER_NAMESPACE_END