Lightweight 0.20250904.0
Loading...
Searching...
No Matches
BasicStringBinder.hpp
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4
5#include "Core.hpp"
6#include "UnicodeConverter.hpp"
7
8#include <cassert>
9#include <memory>
10#include <utility>
11
12namespace Lightweight
13{
14
15/// @brief Magic number, which is used to determine the optimal maximum size of a column.
16///
17/// Columns may be larger than this value, but this is the optimal maximum size for performance,
18/// and usually also means that values are stored in the same row as the rest of the data, or not.
19constexpr std::size_t SqlOptimalMaxColumnSize = 4000;
20
21namespace detail
22{
23 /// Helper function to get raw column data of an array-like type.
24 ///
25 /// @param stmt The SQL statement handle.
26 /// @param column The column number to retrieve data from.
27 /// @param result The array-like type to store the data in.
28 /// @param indicator The indicator to store the length of the data.
29 ///
30 /// @return SQLRETURN indicating the result of the operation.
31 template <auto CType, typename ArrayType>
32 requires requires(ArrayType& arr) {
33 { arr.data() } -> std::convertible_to<typename ArrayType::value_type*>;
34 { arr.size() } -> std::convertible_to<std::size_t>;
35 { arr.resize(std::declval<std::size_t>()) };
36 }
37 static SQLRETURN GetRawColumnArrayData(SQLHSTMT stmt, SQLUSMALLINT column, ArrayType* result, SQLLEN* indicator) noexcept
38 {
39 using CharType = typename ArrayType::value_type;
40
41 *indicator = 0;
42
43 // Resize the string to the size of the data
44 // Get the data (take care of SQL_NULL_DATA and SQL_NO_TOTAL)
45 auto sqlResult = SQLGetData(stmt,
46 column,
47 CType,
48 static_cast<SQLPOINTER>(result->data()),
49 static_cast<SQLLEN>(result->size() * sizeof(CharType)),
50 indicator);
51
52 if (sqlResult == SQL_SUCCESS || sqlResult == SQL_NO_DATA)
53 {
54 // Data has been read successfully on first call to SQLGetData, or there is no data to read.
55 if (*indicator == SQL_NULL_DATA)
56 result->clear();
57 else
58 result->resize(*indicator / sizeof(CharType));
59 return sqlResult;
60 }
61
62 if (sqlResult == SQL_SUCCESS_WITH_INFO && *indicator > static_cast<SQLLEN>(result->size()))
63 {
64 // We have a truncation and the server knows how much data is left.
65 auto const totalCharCount = *indicator / sizeof(CharType);
66 auto const charsWritten = result->size() - 1;
67 result->resize(totalCharCount + 1);
68 auto* bufferCont = result->data() + charsWritten;
69 auto const bufferCharsAvailable = (totalCharCount + 1) - charsWritten;
70 sqlResult = SQLGetData(stmt, column, CType, bufferCont, bufferCharsAvailable * sizeof(CharType), indicator);
71 if (SQL_SUCCEEDED(sqlResult))
72 result->resize(charsWritten + (*indicator / sizeof(CharType)));
73 return sqlResult;
74 }
75
76 size_t writeIndex = 0;
77 while (sqlResult == SQL_SUCCESS_WITH_INFO && *indicator == SQL_NO_TOTAL)
78 {
79 // We have a truncation and the server does not know how much data is left.
80 writeIndex += result->size() - 1;
81 result->resize(result->size() * 2);
82 auto* const bufferStart = result->data() + writeIndex;
83 size_t const bufferCharsAvailable = result->size() - writeIndex;
84 sqlResult = SQLGetData(stmt, column, CType, bufferStart, bufferCharsAvailable, indicator);
85 }
86 return sqlResult;
87 }
88
89 template <typename Utf16StringType>
90 SQLRETURN GetColumnUtf16(SQLHSTMT stmt,
91 SQLUSMALLINT column,
92 Utf16StringType* result,
93 SQLLEN* indicator,
94 SqlDataBinderCallback const& /*cb*/) noexcept
95 {
96 if constexpr (requires { Utf16StringType::Capacity; })
97 result->resize(Utf16StringType::Capacity);
98 else if (result->size() == 0)
99 result->resize(255);
100
101 return GetRawColumnArrayData<SQL_C_WCHAR>(stmt, column, result, indicator);
102 }
103
104 template <typename StringType>
105 SQLRETURN BindOutputColumnNonUtf16Unicode(
106 SQLHSTMT stmt, SQLUSMALLINT column, StringType* result, SQLLEN* indicator, SqlDataBinderCallback& cb) noexcept
107 {
108 using CharType = typename StringType::value_type;
109
110 auto u16String = std::make_shared<std::u16string>();
111 if (!result->empty())
112 u16String->resize(result->size());
113 else
114 u16String->resize(255);
115
116 cb.PlanPostProcessOutputColumn([stmt, column, result, indicator, u16String = u16String]() {
117 if (*indicator == SQL_NULL_DATA)
118 u16String->clear();
119 else if (*indicator == SQL_NO_TOTAL)
120 ; // We don't know the size of the data
121 else if (std::cmp_less_equal(*indicator, u16String->size() * sizeof(char16_t)))
122 u16String->resize(*indicator / sizeof(char16_t));
123 else
124 {
125 auto const totalCharsRequired = static_cast<SQLLEN>(*indicator / sizeof(char16_t));
126 *indicator += sizeof(char16_t); // Add space to hold the null terminator
127 u16String->resize(totalCharsRequired);
128 auto const sqlResult = SQLGetData(stmt, column, SQL_C_WCHAR, u16String->data(), *indicator, indicator);
129 (void) sqlResult;
130 assert(SQL_SUCCEEDED(sqlResult));
131 assert(std::cmp_equal(*indicator, totalCharsRequired * sizeof(char16_t)));
132 }
133
134 if constexpr (sizeof(typename StringType::value_type) == 1)
135 *result = ToUtf8(*u16String);
136 else if constexpr (sizeof(typename StringType::value_type) == 4)
137 {
138 // *result = ToUtf32(*u16String);
139 auto const u32String = ToUtf32(*u16String);
140 *result = StringType {
141 (CharType const*) u32String.data(),
142 (CharType const*) u32String.data() + u32String.size(),
143 };
144 }
145 });
146
147 return SQLBindCol(stmt,
148 column,
149 SQL_C_WCHAR,
150 static_cast<SQLPOINTER>(u16String->data()),
151 static_cast<SQLLEN>(u16String->size() * sizeof(char16_t)),
152 indicator);
153 }
154
155} // namespace detail
156
157// SqlDataBinder<> specialization for ANSI character strings
158template <typename AnsiStringType>
159 requires SqlBasicStringBinderConcept<AnsiStringType, char>
160struct SqlDataBinder<AnsiStringType>
161{
162 using ValueType = AnsiStringType;
163 using CharType = char;
164 using StringTraits = SqlBasicStringOperations<AnsiStringType>;
165
166 static constexpr auto ColumnType = StringTraits::ColumnType;
167
168 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN InputParameter(SQLHSTMT stmt,
169 SQLUSMALLINT column,
170 AnsiStringType const& value,
171 SqlDataBinderCallback& /*cb*/) noexcept
172 {
173 auto const charCount = StringTraits::Size(&value);
174 auto const sqlType = static_cast<SQLSMALLINT>(charCount > SqlOptimalMaxColumnSize ? SQL_LONGVARCHAR : SQL_VARCHAR);
175 return SQLBindParameter(stmt,
176 column,
177 SQL_PARAM_INPUT,
178 SQL_C_CHAR,
179 sqlType,
180 charCount,
181 0,
182 (SQLPOINTER) StringTraits::Data(&value),
183 sizeof(AnsiStringType),
184 nullptr);
185 }
186
187 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN OutputColumn(
188 SQLHSTMT stmt, SQLUSMALLINT column, AnsiStringType* result, SQLLEN* indicator, SqlDataBinderCallback& cb) noexcept
189 {
190 if constexpr (requires { AnsiStringType::Capacity; })
191 StringTraits::Resize(result, AnsiStringType::Capacity);
192 else if (StringTraits::Size(result) == 0)
193 StringTraits::Resize(result, 255);
194
195 if constexpr (requires { StringTraits::PostProcessOutputColumn(result, *indicator); })
196 cb.PlanPostProcessOutputColumn(
197 [indicator, result]() { StringTraits::PostProcessOutputColumn(result, *indicator); });
198 else
199 cb.PlanPostProcessOutputColumn(
200 [stmt, column, indicator, result]() { PostProcessOutputColumn(stmt, column, result, indicator); });
201
202 return SQLBindCol(stmt,
203 column,
204 SQL_C_CHAR,
205 (SQLPOINTER) StringTraits::Data(result),
206 (SQLLEN) StringTraits::Size(result),
207 indicator);
208 }
209
210 static void PostProcessOutputColumn(SQLHSTMT stmt, SQLUSMALLINT column, AnsiStringType* result, SQLLEN* indicator)
211 {
212 // Now resize the string to the actual length of the data
213 // NB: If the indicator is greater than the buffer size, we have a truncation.
214 if (*indicator == SQL_NO_TOTAL)
215 {
216 // We have a truncation and the server does not know how much data is left.
217 StringTraits::Resize(result, StringTraits::Size(result) - 1);
218 }
219 else if (*indicator == SQL_NULL_DATA)
220 {
221 // We have a NULL value
222 StringTraits::Resize(result, 0);
223 }
224 else if (*indicator <= static_cast<SQLLEN>(StringTraits::Size(result)))
225 {
226 StringTraits::Resize(result, static_cast<size_t>(*indicator));
227 }
228 else
229 {
230 // We have a truncation and the server knows how much data is left.
231 // Extend the buffer and fetch the rest via SQLGetData.
232
233 auto const totalCharsRequired = *indicator;
234 StringTraits::Resize(result, totalCharsRequired + 1);
235 auto const sqlResult =
236 SQLGetData(stmt, column, SQL_C_CHAR, StringTraits::Data(result), totalCharsRequired + 1, indicator);
237 (void) sqlResult;
238 assert(SQL_SUCCEEDED(sqlResult));
239 assert(*indicator == totalCharsRequired);
240 StringTraits::Resize(result, totalCharsRequired);
241 }
242 }
243
244 // NOLINTNEXTLINE(readability-function-cognitive-complexity)
245 static SQLRETURN GetColumn(SQLHSTMT stmt,
246 SQLUSMALLINT column,
247 AnsiStringType* result,
248 SQLLEN* indicator,
249 SqlDataBinderCallback const& /*cb*/) noexcept
250 {
251 if constexpr (requires { AnsiStringType::Capacity; })
252 {
253 StringTraits::Resize(result, AnsiStringType::Capacity);
254 SQLRETURN const rv =
255 SQLGetData(stmt, column, SQL_C_CHAR, StringTraits::Data(result), AnsiStringType::Capacity, indicator);
256 if (rv == SQL_SUCCESS || rv == SQL_NO_DATA)
257 {
258 if (*indicator == SQL_NULL_DATA)
259 StringTraits::Resize(result, 0);
260 else if (*indicator != SQL_NO_TOTAL)
261 StringTraits::Resize(result, (std::min) (AnsiStringType::Capacity, static_cast<size_t>(*indicator)));
262 }
263 if constexpr (requires { StringTraits::PostProcessOutputColumn(result, *indicator); })
264 StringTraits::PostProcessOutputColumn(result, *indicator);
265 return rv;
266 }
267 else
268 {
269 StringTraits::Reserve(result, 15);
270 size_t writeIndex = 0;
271 *indicator = 0;
272 while (true)
273 {
274 auto* const bufferStart = StringTraits::Data(result) + writeIndex;
275 size_t const bufferSize = StringTraits::Size(result) - writeIndex;
276 SQLRETURN const rv = SQLGetData(stmt, column, SQL_C_CHAR, bufferStart, bufferSize, indicator);
277 switch (rv)
278 {
279 case SQL_SUCCESS:
280 case SQL_NO_DATA:
281 // last successive call
282 if (*indicator != SQL_NULL_DATA)
283 {
284 StringTraits::Resize(result, writeIndex + *indicator);
285 *indicator = StringTraits::Size(result);
286 }
287 return SQL_SUCCESS;
288 case SQL_SUCCESS_WITH_INFO: {
289 // more data pending
290 if (*indicator == SQL_NO_TOTAL)
291 {
292 // We have a truncation and the server does not know how much data is left.
293 writeIndex += bufferSize - 1;
294 StringTraits::Resize(result, (2 * writeIndex) + 1);
295 }
296 else if (std::cmp_greater_equal(*indicator, bufferSize))
297 {
298 // We have a truncation and the server knows how much data is left.
299 writeIndex += bufferSize - 1;
300 StringTraits::Resize(result, writeIndex + *indicator);
301 }
302 else
303 {
304 // We have no truncation and the server knows how much data is left.
305 StringTraits::Resize(result, writeIndex + *indicator - 1);
306 return SQL_SUCCESS;
307 }
308 break;
309 }
310 default:
311 if constexpr (requires { StringTraits::PostProcessOutputColumn(result, *indicator); })
312 StringTraits::PostProcessOutputColumn(result, *indicator);
313 return rv;
314 }
315 }
316 }
317 }
318
319 static LIGHTWEIGHT_FORCE_INLINE std::string_view Inspect(AnsiStringType const& value) noexcept
320 {
321 return { StringTraits::Data(&value), StringTraits::Size(&value) };
322 }
323};
324
325// SqlDataBinder<> specialization for UTF-16 strings
326template <typename Utf16StringType>
327 requires(SqlBasicStringBinderConcept<Utf16StringType, char16_t>
328 || (SqlBasicStringBinderConcept<Utf16StringType, unsigned short>)
329 || (SqlBasicStringBinderConcept<Utf16StringType, wchar_t> && sizeof(wchar_t) == 2))
330struct SqlDataBinder<Utf16StringType>
331{
332 using ValueType = Utf16StringType;
333 using CharType = std::remove_cvref_t<decltype(std::declval<Utf16StringType>()[0])>;
334 using StringTraits = SqlBasicStringOperations<Utf16StringType>;
335
336 static constexpr auto ColumnType = StringTraits::ColumnType;
337
338 static constexpr auto CType = SQL_C_WCHAR;
339
340 static SQLRETURN InputParameter(SQLHSTMT stmt,
341 SQLUSMALLINT column,
342 Utf16StringType const& value,
343 SqlDataBinderCallback& cb) noexcept
344 {
345 switch (cb.ServerType())
346 {
347 case SqlServerType::POSTGRESQL: {
348 // PostgreSQL only supports UTF-8 as Unicode encoding
349 auto u8String = std::make_shared<std::u8string>(ToUtf8(detail::SqlViewHelper<Utf16StringType>::View(value)));
350 cb.PlanPostExecuteCallback([u8String = u8String]() {}); // Keep the string alive
351 return SQLBindParameter(stmt,
352 column,
353 SQL_PARAM_INPUT,
354 SQL_C_CHAR,
355 SQL_VARCHAR,
356 u8String->size(),
357 0,
358 (SQLPOINTER) u8String->data(),
359 0,
360 nullptr);
361 }
362 case SqlServerType::ORACLE:
363 case SqlServerType::MYSQL:
364 case SqlServerType::SQLITE: // We assume UTF-16 for SQLite
365 case SqlServerType::MICROSOFT_SQL:
366 case SqlServerType::UNKNOWN: {
367 using CharType = StringTraits::CharType;
368 auto const* data = StringTraits::Data(&value);
369 auto const sizeInBytes = StringTraits::Size(&value) * sizeof(CharType);
370 auto const charCount = StringTraits::Size(&value);
371 auto const sqlType =
372 static_cast<SQLSMALLINT>(charCount > SqlOptimalMaxColumnSize ? SQL_WLONGVARCHAR : SQL_WVARCHAR);
373 return SQLBindParameter(
374 stmt, column, SQL_PARAM_INPUT, CType, sqlType, charCount, 0, (SQLPOINTER) data, sizeInBytes, nullptr);
375 }
376 }
377 std::unreachable();
378 }
379
380 static SQLRETURN OutputColumn(
381 SQLHSTMT stmt, SQLUSMALLINT column, Utf16StringType* result, SQLLEN* indicator, SqlDataBinderCallback& cb) noexcept
382 {
383 if constexpr (requires { Utf16StringType::Capacity; })
384 StringTraits::Resize(result, Utf16StringType::Capacity);
385 else if (StringTraits::Size(result) == 0)
386 StringTraits::Resize(result, 255);
387
388 if constexpr (requires { StringTraits::PostProcessOutputColumn(result, *indicator); })
389 {
390 cb.PlanPostProcessOutputColumn(
391 [indicator, result]() { StringTraits::PostProcessOutputColumn(result, *indicator); });
392 }
393 else
394 {
395 cb.PlanPostProcessOutputColumn([stmt, column, indicator, result]() {
396 // Now resize the string to the actual length of the data
397 // NB: If the indicator is greater than the buffer size, we have a truncation.
398 if (*indicator == SQL_NULL_DATA)
399 StringTraits::Resize(result, 0);
400 else if (*indicator == SQL_NO_TOTAL)
401 ; // We don't know the size of the data
402 else if (*indicator <= static_cast<SQLLEN>(result->size() * sizeof(char16_t)))
403 result->resize(*indicator / sizeof(char16_t));
404 else
405 {
406 auto const totalCharsRequired = static_cast<SQLLEN>(*indicator / sizeof(char16_t));
407 *indicator += sizeof(char16_t); // Add space to hold the null terminator
408 result->resize(totalCharsRequired);
409 auto const sqlResult = SQLGetData(stmt, column, SQL_C_WCHAR, result->data(), *indicator, indicator);
410 (void) sqlResult;
411 assert(SQL_SUCCEEDED(sqlResult));
412 assert(std::cmp_equal(*indicator, totalCharsRequired * sizeof(char16_t)));
413 }
414 });
415 }
416 return SQLBindCol(
417 stmt, column, CType, (SQLPOINTER) StringTraits::Data(result), (SQLLEN) StringTraits::Size(result), indicator);
418 }
419
420 static SQLRETURN GetColumn(SQLHSTMT stmt,
421 SQLUSMALLINT column,
422 Utf16StringType* result,
423 SQLLEN* indicator,
424 SqlDataBinderCallback const& cb) noexcept
425 {
426 return detail::GetColumnUtf16(stmt, column, result, indicator, cb);
427 }
428
429 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(Utf16StringType const& value) noexcept
430 {
431 auto u8String = ToUtf8(detail::SqlViewHelper<Utf16StringType>::View(value));
432 return std::string(reinterpret_cast<char const*>(u8String.data()), u8String.size());
433 }
434};
435
436// SqlDataBinder<> specialization for UTF-32 strings
437template <typename Utf32StringType>
438 requires(SqlBasicStringBinderConcept<Utf32StringType, char32_t>
439 || (SqlBasicStringBinderConcept<Utf32StringType, uint32_t>)
440 || (SqlBasicStringBinderConcept<Utf32StringType, wchar_t> && sizeof(wchar_t) == 4))
441struct SqlDataBinder<Utf32StringType>
442{
443 using ValueType = Utf32StringType;
444 using CharType = typename Utf32StringType::value_type;
445 using StringTraits = SqlBasicStringOperations<Utf32StringType>;
446
447 static constexpr auto ColumnType = StringTraits::ColumnType;
448
449 static SQLRETURN InputParameter(SQLHSTMT stmt,
450 SQLUSMALLINT column,
451 Utf32StringType const& value,
452 SqlDataBinderCallback& cb) noexcept
453 {
454 switch (cb.ServerType())
455 {
456 case SqlServerType::POSTGRESQL: {
457 // PostgreSQL only supports UTF-8 as Unicode encoding
458 auto u8String = std::make_shared<std::u8string>(ToUtf8(detail::SqlViewHelper<Utf32StringType>::View(value)));
459 cb.PlanPostExecuteCallback([u8String = u8String]() {}); // Keep the string alive
460 return SQLBindParameter(stmt,
461 column,
462 SQL_PARAM_INPUT,
463 SQL_C_CHAR,
464 SQL_VARCHAR,
465 u8String->size(),
466 0,
467 (SQLPOINTER) u8String->data(),
468 0,
469 nullptr);
470 }
471 case SqlServerType::ORACLE:
472 case SqlServerType::MYSQL:
473 case SqlServerType::SQLITE: // We assume UTF-16 for SQLite
474 case SqlServerType::MICROSOFT_SQL:
475 case SqlServerType::UNKNOWN: {
476 auto u16String =
477 std::make_shared<std::u16string>(ToUtf16(detail::SqlViewHelper<Utf32StringType>::View(value)));
478 cb.PlanPostExecuteCallback([u8String = u16String]() {}); // Keep the string alive
479 auto const* data = u16String->data();
480 auto const charCount = u16String->size();
481 auto const sizeInBytes = u16String->size() * sizeof(char16_t);
482 auto const CType = SQLSMALLINT { SQL_C_WCHAR };
483 auto const sqlType =
484 static_cast<SQLSMALLINT>(charCount > SqlOptimalMaxColumnSize ? SQL_WLONGVARCHAR : SQL_WVARCHAR);
485 return SQLBindParameter(
486 stmt, column, SQL_PARAM_INPUT, CType, sqlType, charCount, 0, (SQLPOINTER) data, sizeInBytes, nullptr);
487 }
488 }
489 std::unreachable();
490 }
491
492 static SQLRETURN OutputColumn(
493 SQLHSTMT stmt, SQLUSMALLINT column, Utf32StringType* result, SQLLEN* indicator, SqlDataBinderCallback& cb) noexcept
494 {
495 return detail::BindOutputColumnNonUtf16Unicode<Utf32StringType>(stmt, column, result, indicator, cb);
496 }
497
498 static SQLRETURN GetColumn(SQLHSTMT stmt,
499 SQLUSMALLINT column,
500 Utf32StringType* result,
501 SQLLEN* indicator,
502 SqlDataBinderCallback const& cb) noexcept
503 {
504 auto u16String = std::u16string {};
505 auto const sqlResult = detail::GetColumnUtf16(stmt, column, &u16String, indicator, cb);
506 if (!SQL_SUCCEEDED(sqlResult))
507 return sqlResult;
508
509 auto const u32String = ToUtf32(u16String);
510 StringTraits::Resize(result, u32String.size());
511 std::copy_n((CharType const*) u32String.data(), u32String.size(), StringTraits::Data(result));
512
513 return sqlResult;
514 }
515
516 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(Utf32StringType const& value) noexcept
517 {
518 auto u8String = ToUtf8(detail::SqlViewHelper<Utf32StringType>::View(value));
519 return std::string(reinterpret_cast<char const*>(u8String.data()), u8String.size());
520 }
521};
522
523// SqlDataBinder<> specialization for UTF-8 strings
524template <typename Utf8StringType>
525 requires SqlBasicStringBinderConcept<Utf8StringType, char8_t>
526struct SqlDataBinder<Utf8StringType>
527{
528 using ValueType = Utf8StringType;
529 using CharType = char8_t;
530 using StringTraits = SqlBasicStringOperations<Utf8StringType>;
531
532 static constexpr auto ColumnType = StringTraits::ColumnType;
533
534 static SQLRETURN InputParameter(SQLHSTMT stmt,
535 SQLUSMALLINT column,
536 Utf8StringType const& value,
537 SqlDataBinderCallback& cb) noexcept
538 {
539 switch (cb.ServerType())
540 {
541 case SqlServerType::POSTGRESQL: {
542 // PostgreSQL only supports UTF-8 as Unicode encoding
543 return SQLBindParameter(stmt,
544 column,
545 SQL_PARAM_INPUT,
546 SQL_C_CHAR,
547 SQL_VARCHAR,
548 value.size(),
549 0,
550 (SQLPOINTER) value.data(),
551 0,
552 nullptr);
553 }
554 case SqlServerType::ORACLE:
555 case SqlServerType::MYSQL:
556 case SqlServerType::SQLITE: // We assume UTF-16 for SQLite
557 case SqlServerType::MICROSOFT_SQL:
558 case SqlServerType::UNKNOWN: {
559 auto u16String =
560 std::make_shared<std::u16string>(ToUtf16(detail::SqlViewHelper<Utf8StringType>::View(value)));
561 cb.PlanPostExecuteCallback([u16String = u16String]() {}); // Keep the string alive
562
563 auto const CType = SQL_C_WCHAR;
564 auto const charCount = u16String->size();
565 auto const byteCount = u16String->size() * sizeof(char16_t);
566 auto const sqlType =
567 static_cast<SQLSMALLINT>(charCount > SqlOptimalMaxColumnSize ? SQL_WLONGVARCHAR : SQL_WVARCHAR);
568 return SQLBindParameter(stmt,
569 column,
570 SQL_PARAM_INPUT,
571 CType,
572 sqlType,
573 charCount,
574 0,
575 (SQLPOINTER) u16String->data(),
576 byteCount,
577 nullptr);
578 }
579 }
580 std::unreachable();
581 }
582
583 static SQLRETURN OutputColumn(
584 SQLHSTMT stmt, SQLUSMALLINT column, Utf8StringType* result, SQLLEN* indicator, SqlDataBinderCallback& cb) noexcept
585 {
586 return detail::BindOutputColumnNonUtf16Unicode<Utf8StringType>(stmt, column, result, indicator, cb);
587 }
588
589 static SQLRETURN GetColumn(SQLHSTMT stmt,
590 SQLUSMALLINT column,
591 Utf8StringType* result,
592 SQLLEN* indicator,
593 SqlDataBinderCallback const& cb) noexcept
594 {
595 auto u16String = std::u16string {};
596 u16String.resize(result->size());
597 auto const sqlReturn = detail::GetColumnUtf16(stmt, column, &u16String, indicator, cb);
598 if (SQL_SUCCEEDED(sqlReturn))
599 *result = ToUtf8(u16String);
600 return sqlReturn;
601 }
602
603 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(Utf8StringType const& value) noexcept
604 {
605 // Pass data as-is
606 return std::string(reinterpret_cast<char const*>(value.data()), value.size());
607 }
608};
609
610} // namespace Lightweight
T ToUtf32(std::u8string_view u8InputString)
LIGHTWEIGHT_API std::u8string ToUtf8(std::u32string_view u32InputString)
std::u16string ToUtf16(std::basic_string_view< T > const u32InputString)