Lightweight 0.20250904.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 BaseType _base;
26
27 public:
28 using value_type = uint8_t;
29 static constexpr auto ColumnType = SqlColumnTypeDefinitions::VarBinary { N };
30
31 /// The maximum size of the underlying data storage.
32 static constexpr std::size_t DynamicCapacity = N;
33
34 LIGHTWEIGHT_FORCE_INLINE constexpr SqlDynamicBinary() noexcept = default;
35 LIGHTWEIGHT_FORCE_INLINE constexpr SqlDynamicBinary(SqlDynamicBinary const&) noexcept = default;
36 LIGHTWEIGHT_FORCE_INLINE constexpr SqlDynamicBinary& operator=(SqlDynamicBinary const&) noexcept = default;
37 LIGHTWEIGHT_FORCE_INLINE constexpr SqlDynamicBinary(SqlDynamicBinary&&) noexcept = default;
38 LIGHTWEIGHT_FORCE_INLINE constexpr SqlDynamicBinary& operator=(SqlDynamicBinary&&) noexcept = default;
39 LIGHTWEIGHT_FORCE_INLINE constexpr ~SqlDynamicBinary() noexcept = default;
40
41 /// Constructs a fixed-size string from a string literal.
42 template <std::size_t SourceSize>
43 constexpr LIGHTWEIGHT_FORCE_INLINE SqlDynamicBinary(value_type const (&text)[SourceSize]):
44 _base { text, text + SourceSize }
45 {
46 static_assert(SourceSize <= N + 1, "RHS string size must not exceed target string's capacity.");
47 }
48
49 /// Constructs the object from an initializer list of bytes.
50 template <std::size_t SourceSize>
51 constexpr LIGHTWEIGHT_FORCE_INLINE SqlDynamicBinary(std::initializer_list<value_type> data):
52 _base { data }
53 {
54 static_assert(SourceSize <= N + 1, "RHS string size must not exceed target string's capacity.");
55 }
56
57 /// Constructs a fixed-size string from a string pointer and end pointer.
58 LIGHTWEIGHT_FORCE_INLINE constexpr SqlDynamicBinary(value_type const* s, value_type const* e) noexcept:
59 _base { s, e }
60 {
61 }
62#if !defined(LIGHTWEIGHT_CXX26_REFLECTION)
63 /// Constructs a fixed-size string from a string view.
64 LIGHTWEIGHT_FORCE_INLINE constexpr SqlDynamicBinary(std::basic_string_view<value_type> s) noexcept:
65 _base { static_cast<uint8_t const*>(s.data()), static_cast<uint8_t const*>(s.data() + s.size()) }
66 {
67 }
68
69 /// Retrieves a string view of the string.
70 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr std::basic_string_view<value_type> ToStringView() const noexcept
71 {
72 return { _base.data(), _base.size() };
73 }
74#endif
75
76 constexpr auto operator<=>(SqlDynamicBinary<N> const&) const noexcept = default;
77
78 /// Retrieves the size of the string.
79 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr std::size_t size() const noexcept
80 {
81 return _base.size();
82 }
83
84 /// Resizes the underlying data storage to the given size.
85 void LIGHTWEIGHT_FORCE_INLINE resize(std::size_t newSize)
86 {
87 _base.resize(newSize);
88 }
89
90 /// Tests if the stored data is empty.
91 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr bool empty() const noexcept
92 {
93 return _base.empty();
94 }
95
96 /// Retrieves the pointer to the string data.
97 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr decltype(auto) data(this auto&& self) noexcept
98 {
99 return self._base.data();
100 }
101
102 /// Clears the storad data.
103 LIGHTWEIGHT_FORCE_INLINE void clear() noexcept
104 {
105 _base.clear();
106 }
107
108 private:
109 mutable SQLLEN _indicator = 0;
110
111 friend struct SqlDataBinder<SqlDynamicBinary<N>>;
112};
113
114namespace detail
115{
116
117 template <typename>
118 struct IsSqlDynamicBinaryImpl: std::false_type
119 {
120 };
121
122 template <size_t T>
123 struct IsSqlDynamicBinaryImpl<SqlDynamicBinary<T>>: std::true_type
124 {
125 };
126
127 template <size_t T>
128 struct IsSqlDynamicBinaryImpl<std::optional<SqlDynamicBinary<T>>>: std::true_type
129 {
130 };
131
132} // namespace detail
133
134template <typename T>
135constexpr bool IsSqlDynamicBinary = detail::IsSqlDynamicBinaryImpl<T>::value;
136
137} // namespace Lightweight
138
139template <std::size_t N>
140struct std::formatter<Lightweight::SqlDynamicBinary<N>>: std::formatter<std::string>
141{
142 auto format(Lightweight::SqlDynamicBinary<N> const& text, format_context& ctx) const
143 {
144 std::string humanReadableText;
145 for (auto byte: text)
146 {
147 if (byte >= 0x20 && byte <= 0x7E)
148 humanReadableText += static_cast<char>(byte);
149 else
150 humanReadableText += std::format("\\x{:02X}", byte);
151 }
152 return std::formatter<std::string>::format(humanReadableText.data(), ctx);
153 }
154};
155
156namespace Lightweight
157{
158
159template <std::size_t N>
160struct SqlDataBinder<SqlDynamicBinary<N>>
161{
162 using ValueType = SqlDynamicBinary<N>;
163
164 static constexpr auto ColumnType = SqlColumnTypeDefinitions::VarBinary { N };
165
166 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN InputParameter(SQLHSTMT stmt,
167 SQLUSMALLINT column,
168 ValueType const& value,
169 SqlDataBinderCallback& /*cb*/) noexcept
170 {
171 value._indicator = static_cast<SQLLEN>(value.size());
172 return SQLBindParameter(stmt,
173 column,
174 SQL_PARAM_INPUT,
175 SQL_C_BINARY,
176 SQL_LONGVARBINARY,
177 value.size(),
178 0,
179 (SQLPOINTER) value.data(),
180 0,
181 &value._indicator);
182 }
183
184 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN OutputColumn(
185 SQLHSTMT stmt, SQLUSMALLINT column, ValueType* result, SQLLEN* indicator, SqlDataBinderCallback& cb) noexcept
186 {
187 if (result->empty())
188 result->resize(255);
189
190 cb.PlanPostProcessOutputColumn([stmt, column, result, indicator]() {
191 if (*indicator == SQL_NULL_DATA)
192 // The data is NULL.
193 result->clear();
194 else if (*indicator == SQL_NO_TOTAL)
195 // We have a truncation and the server does not know how much data is left.
196 result->resize(result->size() - 1);
197 else if (*indicator <= static_cast<SQLLEN>(result->size()))
198 result->resize(*indicator);
199 else
200 {
201 // We have a truncation and the server knows how much data is left.
202 // Extend the buffer and fetch the rest via SQLGetData.
203
204 auto const totalCharsRequired = *indicator;
205 result->resize(totalCharsRequired + 1);
206 auto const sqlResult =
207 SQLGetData(stmt, column, SQL_C_BINARY, (SQLPOINTER) result->data(), totalCharsRequired + 1, indicator);
208 (void) sqlResult;
209 assert(SQL_SUCCEEDED(sqlResult));
210 assert(*indicator == totalCharsRequired);
211 result->resize(totalCharsRequired);
212 }
213 });
214
215 return SQLBindCol(stmt, column, SQL_C_BINARY, (SQLPOINTER) result->data(), 255, indicator);
216 }
217
218 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN GetColumn(SQLHSTMT stmt,
219 SQLUSMALLINT column,
220 ValueType* result,
221 SQLLEN* indicator,
222 SqlDataBinderCallback const& /*cb*/) noexcept
223 {
224 if (result->empty())
225 result->resize(255);
226
227 return detail::GetRawColumnArrayData<SQL_C_BINARY>(stmt, column, result, indicator);
228 }
229
230 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(ValueType const& value)
231 {
232 return std::format("SqlBinary<{}>(size={})", N, value.size());
233 }
234};
235
236} // 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.
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.
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.
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.