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