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