userver: userver/storages/postgres/detail/query_parameters.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
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 parameters.push_back({});
196 auto& buffer = parameters.back();
197 io::WriteBuffer(types, buffer, arg);
198 auto size = buffer.size();
199 param_lengths.push_back(size);
200 if (size == 0) {
201 param_buffers.push_back(empty_buffer);
202 } else {
203 param_buffers.push_back(buffer.data());
204 }
205 }
206
207 using OidList = std::vector<Oid>;
208 using BufferType = std::vector<char>;
209 using ParameterList = std::vector<BufferType>;
210 using IntList = std::vector<int>;
211
212 static constexpr const char* empty_buffer = "";
213
214 ParameterList parameters; // TODO Replace with a single buffer
215 OidList param_types;
216 std::vector<const char*> param_buffers;
217 IntList param_lengths;
218 IntList param_formats;
219};
220
221} // namespace storages::postgres::detail
222
223USERVER_NAMESPACE_END