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