Lightweight 0.20260303.0
Loading...
Searching...
No Matches
SqlDynamicBinary.hpp
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4
5#include "../SqlColumnTypeDefinitions.hpp"
6#include "BasicStringBinder.hpp"
7#include "Core.hpp"
8
9#include <cassert>
10#include <format>
11#include <string>
12
13namespace Lightweight
14{
15
16/// SQL dynamic-capacity string that mimicks standard library string.
17///
18/// The underlying memory is allocated dynamically and the string can grow up to the maximum size of a SQL column.
19///
20/// @ingroup DataTypes
21template <std::size_t N>
23{
24 using BaseType = std::vector<uint8_t>;
25
26 public:
27 /// The element type of the binary data.
28 using value_type = uint8_t;
29 /// The SQL column type definition for this binary type.
30 static constexpr auto ColumnType = SqlColumnTypeDefinitions::VarBinary { N };
31
32 /// The maximum size of the underlying data storage.
33 static constexpr std::size_t DynamicCapacity = N;
34
35 /// Default constructor, creates an empty object.
36 LIGHTWEIGHT_FORCE_INLINE constexpr SqlDynamicBinary() noexcept = default;
37
38 /// Defaulted copy constructor.
39 LIGHTWEIGHT_FORCE_INLINE constexpr SqlDynamicBinary(SqlDynamicBinary const&) noexcept = default;
40
41 /// Defaulted copy assignment operator.
42 LIGHTWEIGHT_FORCE_INLINE constexpr SqlDynamicBinary& operator=(SqlDynamicBinary const&) noexcept = default;
43
44 /// Defaulted move constructor.
45 LIGHTWEIGHT_FORCE_INLINE constexpr SqlDynamicBinary(SqlDynamicBinary&&) noexcept = default;
46
47 /// Defaulted move assignment operator.
48 LIGHTWEIGHT_FORCE_INLINE constexpr SqlDynamicBinary& operator=(SqlDynamicBinary&&) noexcept = default;
49
50 /// Defaulted destructor.
51 LIGHTWEIGHT_FORCE_INLINE constexpr ~SqlDynamicBinary() noexcept = default;
52
53 /// Constructs a fixed-size string from a string literal.
54 template <std::size_t SourceSize>
55 constexpr LIGHTWEIGHT_FORCE_INLINE SqlDynamicBinary(value_type const (&text)[SourceSize]):
56 _base { text, text + SourceSize }
57 {
58 static_assert(SourceSize <= N + 1, "RHS string size must not exceed target string's capacity.");
59 }
60
61 /// Constructs the object from an initializer list of bytes.
62 template <std::size_t SourceSize>
63 constexpr LIGHTWEIGHT_FORCE_INLINE SqlDynamicBinary(std::initializer_list<value_type> data):
64 _base { data }
65 {
66 static_assert(SourceSize <= N + 1, "RHS string size must not exceed target string's capacity.");
67 }
68
69 /// Constructs a fixed-size string from a string pointer and end pointer.
70 LIGHTWEIGHT_FORCE_INLINE constexpr SqlDynamicBinary(value_type const* s, value_type const* e) noexcept:
71 _base { s, e }
72 {
73 }
74#if !defined(LIGHTWEIGHT_CXX26_REFLECTION)
75 /// Constructs a fixed-size string from a string view.
76 LIGHTWEIGHT_FORCE_INLINE constexpr SqlDynamicBinary(std::basic_string_view<value_type> s) noexcept:
77 _base { static_cast<uint8_t const*>(s.data()), static_cast<uint8_t const*>(s.data() + s.size()) }
78 {
79 }
80
81 /// Retrieves a string view of the string.
82 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr std::basic_string_view<value_type> ToStringView() const noexcept
83 {
84 return { _base.data(), _base.size() };
85 }
86#endif
87
88 /// Defaulted Three-way comparison operator.
89 constexpr auto operator<=>(SqlDynamicBinary<N> const&) const noexcept = default;
90
91 /// Retrieves the size of the string.
92 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr std::size_t size() const noexcept
93 {
94 return _base.size();
95 }
96
97 /// Resizes the underlying data storage to the given size.
98 void LIGHTWEIGHT_FORCE_INLINE resize(std::size_t newSize)
99 {
100 _base.resize(newSize);
101 }
102
103 /// Tests if the stored data is empty.
104 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr bool empty() const noexcept
105 {
106 return _base.empty();
107 }
108
109 /// Retrieves the pointer to the string data.
110 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr decltype(auto) data(this auto&& self) noexcept
111 {
112 self.EnsureNonNullBuffer();
113 return self._base.data();
114 }
115
116 /// Clears the storad data.
117 LIGHTWEIGHT_FORCE_INLINE void clear() noexcept
118 {
119 _base.clear();
120 }
121
122 private:
123 void EnsureNonNullBuffer() const
124 {
125 if (_base.data() == nullptr)
126 {
127 const_cast<SqlDynamicBinary<N>*>(this)->_base.resize(8);
128 const_cast<SqlDynamicBinary<N>*>(this)->_base.clear();
129 }
130 }
131
132 BaseType _base;
133 mutable SQLLEN _indicator = 0;
134
135 friend struct SqlDataBinder<SqlDynamicBinary<N>>;
136};
137
138namespace detail
139{
140
141 template <typename>
142 struct IsSqlDynamicBinaryImpl: std::false_type
143 {
144 };
145
146 template <size_t T>
147 struct IsSqlDynamicBinaryImpl<SqlDynamicBinary<T>>: std::true_type
148 {
149 };
150
151 template <size_t T>
152 struct IsSqlDynamicBinaryImpl<std::optional<SqlDynamicBinary<T>>>: std::true_type
153 {
154 };
155
156} // namespace detail
157
158template <typename T>
159constexpr bool IsSqlDynamicBinary = detail::IsSqlDynamicBinaryImpl<T>::value;
160
161} // namespace Lightweight
162
163template <std::size_t N>
164struct std::formatter<Lightweight::SqlDynamicBinary<N>>: std::formatter<std::string>
165{
166 auto format(Lightweight::SqlDynamicBinary<N> const& text, format_context& ctx) const
167 {
168 std::string humanReadableText;
169 for (auto byte: text)
170 {
171 if (byte >= 0x20 && byte <= 0x7E)
172 humanReadableText += static_cast<char>(byte);
173 else
174 humanReadableText += std::format("\\x{:02X}", byte);
175 }
176 return std::formatter<std::string>::format(humanReadableText.data(), ctx);
177 }
178};
179
180namespace Lightweight
181{
182
183template <std::size_t N>
184struct SqlDataBinder<SqlDynamicBinary<N>>
185{
186 using ValueType = SqlDynamicBinary<N>;
187
188 static constexpr auto ColumnType = SqlColumnTypeDefinitions::VarBinary { N };
189
190 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN InputParameter(SQLHSTMT stmt,
191 SQLUSMALLINT column,
192 ValueType const& value,
193 SqlDataBinderCallback& /*cb*/) noexcept
194 {
195 value._indicator = static_cast<SQLLEN>(value.size());
196
197 auto const sqlType = static_cast<SQLSMALLINT>(value.size() > 8000 ? SQL_LONGVARBINARY : SQL_VARBINARY);
198 auto const bufferSize = static_cast<SQLULEN>(value.size());
199 auto* const ptr = (SQLPOINTER) value.data();
200
201 return SQLBindParameter(
202 stmt, column, SQL_PARAM_INPUT, SQL_C_BINARY, sqlType, bufferSize, 0, ptr, 0, &value._indicator);
203 }
204
205 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN OutputColumn(
206 SQLHSTMT stmt, SQLUSMALLINT column, ValueType* result, SQLLEN* indicator, SqlDataBinderCallback& cb) noexcept
207 {
208 if (result->empty())
209 result->resize(255);
210
211 cb.PlanPostProcessOutputColumn([stmt, column, result, indicator]() {
212 if (*indicator == SQL_NULL_DATA)
213 // The data is NULL.
214 result->clear();
215 else if (*indicator == SQL_NO_TOTAL)
216 // We have a truncation and the server does not know how much data is left.
217 result->resize(result->size() - 1);
218 else if (*indicator <= static_cast<SQLLEN>(result->size()))
219 result->resize(static_cast<size_t>(*indicator));
220 else
221 {
222 // We have a truncation and the server knows how much data is left.
223 // Extend the buffer and fetch the rest via SQLGetData.
224
225 auto const totalCharsRequired = *indicator;
226 result->resize(static_cast<size_t>(totalCharsRequired + 1));
227 auto const sqlResult =
228 SQLGetData(stmt, column, SQL_C_BINARY, (SQLPOINTER) result->data(), totalCharsRequired + 1, indicator);
229 (void) sqlResult;
230 assert(SQL_SUCCEEDED(sqlResult));
231 assert(*indicator == totalCharsRequired);
232 result->resize(static_cast<size_t>(totalCharsRequired));
233 }
234 });
235
236 return SQLBindCol(stmt, column, SQL_C_BINARY, (SQLPOINTER) result->data(), 255, indicator);
237 }
238
239 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN GetColumn(SQLHSTMT stmt,
240 SQLUSMALLINT column,
241 ValueType* result,
242 SQLLEN* indicator,
243 SqlDataBinderCallback const& /*cb*/) noexcept
244 {
245 if (result->empty())
246 result->resize(255);
247
248 return detail::GetRawColumnArrayData<SQL_C_BINARY>(stmt, column, result, indicator);
249 }
250
251 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(ValueType const& value)
252 {
253 return std::format("SqlBinary<{}>(size={})", N, value.size());
254 }
255};
256
257} // namespace Lightweight
LIGHTWEIGHT_FORCE_INLINE constexpr SqlDynamicBinary(value_type const *s, value_type const *e) noexcept
Constructs a fixed-size string from a string pointer and end pointer.
static constexpr auto ColumnType
The SQL column type definition for this binary type.
LIGHTWEIGHT_FORCE_INLINE constexpr decltype(auto) data(this auto &&self) noexcept
Retrieves the pointer to the string data.
LIGHTWEIGHT_FORCE_INLINE constexpr bool empty() const noexcept
Tests if the stored data is empty.
LIGHTWEIGHT_FORCE_INLINE void clear() noexcept
Clears the storad data.
LIGHTWEIGHT_FORCE_INLINE constexpr std::size_t size() const noexcept
Retrieves the size of the string.
void LIGHTWEIGHT_FORCE_INLINE resize(std::size_t newSize)
Resizes the underlying data storage to the given size.
uint8_t value_type
The element type of the binary data.
LIGHTWEIGHT_FORCE_INLINE constexpr SqlDynamicBinary() noexcept=default
Default constructor, creates an empty object.
LIGHTWEIGHT_FORCE_INLINE constexpr SqlDynamicBinary(std::basic_string_view< value_type > s) noexcept
Constructs a fixed-size string from a string view.
LIGHTWEIGHT_FORCE_INLINE constexpr std::basic_string_view< value_type > ToStringView() const noexcept
Retrieves a string view of the string.
constexpr auto operator<=>(SqlDynamicBinary< N > const &) const noexcept=default
Defaulted Three-way comparison operator.
static constexpr std::size_t DynamicCapacity
The maximum size of the underlying data storage.
constexpr LIGHTWEIGHT_FORCE_INLINE SqlDynamicBinary(std::initializer_list< value_type > data)
Constructs the object from an initializer list of bytes.