6#include "UnicodeConverter.hpp"
20constexpr inline std::size_t SqlOptimalMaxColumnSize = 4000;
32 template <auto CType,
typename ArrayType>
33 requires requires(ArrayType& arr) {
34 { arr.data() } -> std::convertible_to<typename ArrayType::value_type*>;
35 { arr.size() } -> std::convertible_to<std::size_t>;
36 { arr.resize(std::declval<std::size_t>()) };
38 SQLRETURN GetRawColumnArrayData(SQLHSTMT stmt, SQLUSMALLINT column, ArrayType* result, SQLLEN* indicator)
noexcept
40 using CharType = ArrayType::value_type;
46 auto sqlResult = SQLGetData(stmt,
49 static_cast<SQLPOINTER
>(result->data()),
50 static_cast<SQLLEN
>(result->size() *
sizeof(CharType)),
53 if (sqlResult == SQL_SUCCESS || sqlResult == SQL_NO_DATA)
56 if (*indicator == SQL_NULL_DATA)
59 result->resize(
static_cast<size_t>(*indicator) /
sizeof(CharType));
63 if (sqlResult == SQL_SUCCESS_WITH_INFO && *indicator >
static_cast<SQLLEN
>(result->size()))
66 auto const totalCharCount =
static_cast<size_t>(*indicator) /
sizeof(CharType);
67 auto const charsWritten = result->size() - 1;
68 result->resize(totalCharCount + 1);
69 auto* bufferCont = result->data() + charsWritten;
70 auto const bufferCharsAvailable = (totalCharCount + 1) - charsWritten;
71 sqlResult = SQLGetData(
72 stmt, column, CType, bufferCont,
static_cast<SQLLEN
>(bufferCharsAvailable *
sizeof(CharType)), indicator);
73 if (SQL_SUCCEEDED(sqlResult))
74 result->resize(charsWritten + (
static_cast<size_t>(*indicator) /
sizeof(CharType)));
78 size_t writeIndex = 0;
79 while (sqlResult == SQL_SUCCESS_WITH_INFO && *indicator == SQL_NO_TOTAL)
82 writeIndex += result->size() - 1;
83 result->resize(result->size() * 2);
84 auto*
const bufferStart = result->data() + writeIndex;
85 size_t const bufferCharsAvailable = result->size() - writeIndex;
86 sqlResult = SQLGetData(
87 stmt, column, CType, bufferStart,
static_cast<SQLLEN
>(bufferCharsAvailable *
sizeof(CharType)), indicator);
92 template <
typename Utf16StringType>
93 SQLRETURN GetColumnUtf16(SQLHSTMT stmt,
95 Utf16StringType* result,
97 SqlDataBinderCallback
const& )
noexcept
99 if constexpr (
requires { Utf16StringType::Capacity; })
100 result->resize(Utf16StringType::Capacity);
101 else if (result->size() == 0)
104 return GetRawColumnArrayData<SQL_C_WCHAR>(stmt, column, result, indicator);
107 template <
typename StringType>
108 SQLRETURN BindOutputColumnNonUtf16Unicode(
109 SQLHSTMT stmt, SQLUSMALLINT column, StringType* result, SQLLEN* indicator, SqlDataBinderCallback& cb)
noexcept
111 using CharType = StringType::value_type;
113 auto u16String = std::make_shared<std::u16string>();
114 if (!result->empty())
115 u16String->resize(result->size());
117 u16String->resize(255);
119 cb.PlanPostProcessOutputColumn([stmt, column, result, indicator, u16String = u16String]() {
120 if (*indicator == SQL_NULL_DATA)
122 else if (*indicator == SQL_NO_TOTAL)
124 else if (std::cmp_less_equal(*indicator, u16String->size() *
sizeof(
char16_t)))
125 u16String->resize(
static_cast<size_t>(*indicator) /
sizeof(
char16_t));
128 auto const totalCharsRequired =
static_cast<size_t>(*indicator) /
sizeof(char16_t);
129 *indicator +=
sizeof(char16_t);
130 u16String->resize(totalCharsRequired);
131 auto const sqlResult = SQLGetData(stmt, column, SQL_C_WCHAR, u16String->data(), *indicator, indicator);
133 assert(SQL_SUCCEEDED(sqlResult));
134 assert(std::cmp_equal(*indicator, totalCharsRequired *
sizeof(
char16_t)));
137 if constexpr (
sizeof(
typename StringType::value_type) == 1)
138 *result =
ToUtf8(*u16String);
139 else if constexpr (
sizeof(
typename StringType::value_type) == 4)
142 auto const u32String =
ToUtf32(*u16String);
143 *result = StringType {
144 (CharType
const*) u32String.data(),
145 (CharType
const*) u32String.data() + u32String.size(),
155 using StringTraits = SqlBasicStringOperations<StringType>;
156 if constexpr (
requires { StringTraits::PostProcessOutputColumn(result, *indicator); })
158 if (*indicator != SQL_NULL_DATA && *indicator != SQL_NO_TOTAL)
160 auto const syntheticIndicator =
static_cast<SQLLEN
>(StringTraits::Size(result) *
sizeof(CharType));
161 StringTraits::PostProcessOutputColumn(result, syntheticIndicator);
166 return SQLBindCol(stmt,
169 static_cast<SQLPOINTER
>(u16String->data()),
170 static_cast<SQLLEN
>(u16String->size() *
sizeof(
char16_t)),
177template <
typename AnsiStringType>
178 requires SqlBasicStringBinderConcept<AnsiStringType, char>
179struct SqlDataBinder<AnsiStringType>
181 using ValueType = AnsiStringType;
182 using CharType = char;
183 using StringTraits = SqlBasicStringOperations<AnsiStringType>;
185 static constexpr auto ColumnType = StringTraits::ColumnType;
187 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN InputParameter(SQLHSTMT stmt,
189 AnsiStringType
const& value,
190 SqlDataBinderCallback& cb)
noexcept
201 if (cb.ServerType() == SqlServerType::POSTGRESQL)
203 auto u16String = std::make_shared<std::u16string>(
ToUtf16(std::u8string_view {
204 reinterpret_cast<char8_t const*
>(StringTraits::Data(&value)), StringTraits::Size(&value) }));
205 cb.PlanPostExecuteCallback([u16String = u16String]() {});
206 auto const charCount = u16String->size();
207 auto const byteCount = charCount *
sizeof(char16_t);
209 static_cast<SQLSMALLINT
>(charCount > SqlOptimalMaxColumnSize ? SQL_WLONGVARCHAR : SQL_WVARCHAR);
211 SQLLEN* indicator = cb.ProvideInputIndicator();
212 *indicator =
static_cast<SQLLEN
>(byteCount);
214 return SQLBindParameter(stmt,
221 (SQLPOINTER) u16String->data(),
222 static_cast<SQLLEN
>(byteCount),
226 auto const charCount = StringTraits::Size(&value);
227 auto const sqlType =
static_cast<SQLSMALLINT
>(charCount > SqlOptimalMaxColumnSize ? SQL_LONGVARCHAR : SQL_VARCHAR);
229 SQLLEN* indicator = cb.ProvideInputIndicator();
230 *indicator =
static_cast<SQLLEN
>(charCount);
232 return SQLBindParameter(stmt,
239 (SQLPOINTER) StringTraits::Data(&value),
244 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN BatchInputParameter(SQLHSTMT stmt,
246 AnsiStringType
const* values,
248 SqlDataBinderCallback& cb)
noexcept
251 SQLLEN* indicators = cb.ProvideInputIndicators(rowCount);
252 for (
size_t i = 0; i < rowCount; ++i)
254 auto const len = StringTraits::Size(&values[i]);
255 indicators[i] =
static_cast<SQLLEN
>(len);
260 auto const sqlType =
static_cast<SQLSMALLINT
>(maxLen > SqlOptimalMaxColumnSize ? SQL_LONGVARCHAR : SQL_VARCHAR);
262 return SQLBindParameter(stmt,
270 sizeof(AnsiStringType),
274 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN OutputColumn(
275 SQLHSTMT stmt, SQLUSMALLINT column, AnsiStringType* result, SQLLEN* indicator, SqlDataBinderCallback& cb)
noexcept
277 if constexpr (
requires { AnsiStringType::Capacity; })
278 StringTraits::Resize(result, AnsiStringType::Capacity);
279 else if (StringTraits::Size(result) == 0)
280 StringTraits::Resize(result, 255);
282 if constexpr (
requires { StringTraits::PostProcessOutputColumn(result, *indicator); })
283 cb.PlanPostProcessOutputColumn(
284 [indicator, result]() { StringTraits::PostProcessOutputColumn(result, *indicator); });
286 cb.PlanPostProcessOutputColumn(
287 [stmt, column, indicator, result]() { PostProcessOutputColumn(stmt, column, result, indicator); });
289 return SQLBindCol(stmt,
292 (SQLPOINTER) StringTraits::Data(result),
293 (SQLLEN) StringTraits::Size(result),
297 static void PostProcessOutputColumn(SQLHSTMT stmt, SQLUSMALLINT column, AnsiStringType* result, SQLLEN* indicator)
301 if (*indicator == SQL_NO_TOTAL)
304 StringTraits::Resize(result,
static_cast<SQLLEN
>(StringTraits::Size(result)) - 1);
306 else if (*indicator == SQL_NULL_DATA)
309 StringTraits::Resize(result, 0);
311 else if (*indicator <=
static_cast<SQLLEN
>(StringTraits::Size(result)))
313 StringTraits::Resize(result, *indicator);
320 auto const totalCharsRequired = *indicator;
321 StringTraits::Resize(result, totalCharsRequired + 1);
322 auto const sqlResult =
323 SQLGetData(stmt, column, SQL_C_CHAR, StringTraits::Data(result), totalCharsRequired + 1, indicator);
325 assert(SQL_SUCCEEDED(sqlResult));
326 assert(*indicator == totalCharsRequired);
327 StringTraits::Resize(result, totalCharsRequired);
332 static SQLRETURN GetColumn(SQLHSTMT stmt,
334 AnsiStringType* result,
336 SqlDataBinderCallback
const& )
noexcept
338 if constexpr (
requires { AnsiStringType::Capacity; })
340 StringTraits::Resize(result, AnsiStringType::Capacity);
342 SQLGetData(stmt, column, SQL_C_CHAR, StringTraits::Data(result), AnsiStringType::Capacity, indicator);
343 if (rv == SQL_SUCCESS || rv == SQL_NO_DATA)
345 if (*indicator == SQL_NULL_DATA)
346 StringTraits::Resize(result, 0);
347 else if (*indicator != SQL_NO_TOTAL)
348 StringTraits::Resize(
349 result,
static_cast<SQLLEN
>((std::min) (AnsiStringType::Capacity,
static_cast<size_t>(*indicator))));
351 if constexpr (
requires { StringTraits::PostProcessOutputColumn(result, *indicator); })
352 StringTraits::PostProcessOutputColumn(result, *indicator);
357 StringTraits::Reserve(result, 15);
358 size_t writeIndex = 0;
362 auto*
const bufferStart = StringTraits::Data(result) + writeIndex;
363 size_t const bufferSize = StringTraits::Size(result) - writeIndex;
365 SQLGetData(stmt, column, SQL_C_CHAR, bufferStart,
static_cast<SQLLEN
>(bufferSize), indicator);
371 if (*indicator != SQL_NULL_DATA)
373 StringTraits::Resize(result,
static_cast<SQLLEN
>(writeIndex) + *indicator);
374 *indicator =
static_cast<SQLLEN
>(StringTraits::Size(result));
377 case SQL_SUCCESS_WITH_INFO: {
379 if (*indicator == SQL_NO_TOTAL)
382 writeIndex += bufferSize - 1;
383 StringTraits::Resize(result,
static_cast<SQLLEN
>((2 * writeIndex) + 1));
385 else if (std::cmp_greater_equal(*indicator, bufferSize))
388 writeIndex += bufferSize - 1;
389 StringTraits::Resize(result,
static_cast<SQLLEN
>(writeIndex) + *indicator);
394 StringTraits::Resize(result,
static_cast<SQLLEN
>(writeIndex) + *indicator - 1);
400 if constexpr (
requires { StringTraits::PostProcessOutputColumn(result, *indicator); })
401 StringTraits::PostProcessOutputColumn(result, *indicator);
408 static LIGHTWEIGHT_FORCE_INLINE std::string_view Inspect(AnsiStringType
const& value)
noexcept
410 return { StringTraits::Data(&value), StringTraits::Size(&value) };
415template <
typename Utf16StringType>
416 requires(SqlBasicStringBinderConcept<Utf16StringType, char16_t>
417 || (SqlBasicStringBinderConcept<Utf16StringType, unsigned short>)
418 || (SqlBasicStringBinderConcept<Utf16StringType, wchar_t> &&
sizeof(
wchar_t) == 2))
419struct SqlDataBinder<Utf16StringType>
421 using ValueType = Utf16StringType;
422 using CharType = std::remove_cvref_t<decltype(std::declval<Utf16StringType>()[0])>;
423 using StringTraits = SqlBasicStringOperations<Utf16StringType>;
425 static constexpr auto ColumnType = StringTraits::ColumnType;
427 static constexpr auto CType = SQL_C_WCHAR;
429 static SQLRETURN InputParameter(SQLHSTMT stmt,
431 Utf16StringType
const& value,
432 SqlDataBinderCallback& cb)
noexcept
437 using CharType = StringTraits::CharType;
438 auto const* data = StringTraits::Data(&value);
439 auto const sizeInBytes = StringTraits::Size(&value) *
sizeof(CharType);
440 auto const charCount = StringTraits::Size(&value);
441 auto const sqlType =
static_cast<SQLSMALLINT
>(charCount > SqlOptimalMaxColumnSize ? SQL_WLONGVARCHAR : SQL_WVARCHAR);
443 SQLLEN* indicator = cb.ProvideInputIndicator();
444 *indicator =
static_cast<SQLLEN
>(sizeInBytes);
446 return SQLBindParameter(stmt,
454 static_cast<SQLLEN
>(sizeInBytes),
458 static SQLRETURN OutputColumn(
459 SQLHSTMT stmt, SQLUSMALLINT column, Utf16StringType* result, SQLLEN* indicator, SqlDataBinderCallback& cb)
noexcept
461 if constexpr (
requires { Utf16StringType::Capacity; })
462 StringTraits::Resize(result, Utf16StringType::Capacity);
463 else if (StringTraits::Size(result) == 0)
464 StringTraits::Resize(result, 255);
466 if constexpr (
requires { StringTraits::PostProcessOutputColumn(result, *indicator); })
468 cb.PlanPostProcessOutputColumn(
469 [indicator, result]() { StringTraits::PostProcessOutputColumn(result, *indicator); });
473 cb.PlanPostProcessOutputColumn([stmt, column, indicator, result]() {
476 if (*indicator == SQL_NULL_DATA)
477 StringTraits::Resize(result, 0);
478 else if (*indicator == SQL_NO_TOTAL)
480 else if (*indicator <=
static_cast<SQLLEN
>(result->size() *
sizeof(
char16_t)))
481 result->resize(
static_cast<size_t>(*indicator) /
sizeof(
char16_t));
484 auto const totalCharsRequired =
static_cast<size_t>(*indicator) /
sizeof(char16_t);
485 *indicator +=
sizeof(char16_t);
486 result->resize(totalCharsRequired);
487 auto const sqlResult = SQLGetData(stmt, column, SQL_C_WCHAR, result->data(), *indicator, indicator);
489 assert(SQL_SUCCEEDED(sqlResult));
490 assert(std::cmp_equal(*indicator, totalCharsRequired *
sizeof(
char16_t)));
494 return SQLBindCol(stmt,
497 (SQLPOINTER) StringTraits::Data(result),
498 (SQLLEN) ((StringTraits::Size(result) + 1) *
sizeof(CharType)),
502 static SQLRETURN GetColumn(SQLHSTMT stmt,
504 Utf16StringType* result,
506 SqlDataBinderCallback
const& cb)
noexcept
508 return detail::GetColumnUtf16(stmt, column, result, indicator, cb);
511 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(Utf16StringType
const& value)
noexcept
513 auto u8String =
ToUtf8(detail::SqlViewHelper<Utf16StringType>::View(value));
514 return std::string(
reinterpret_cast<char const*
>(u8String.data()), u8String.size());
519template <
typename Utf32StringType>
520 requires(SqlBasicStringBinderConcept<Utf32StringType, char32_t>
521 || (SqlBasicStringBinderConcept<Utf32StringType, uint32_t>)
522 || (SqlBasicStringBinderConcept<Utf32StringType, wchar_t> &&
sizeof(
wchar_t) == 4))
523struct SqlDataBinder<Utf32StringType>
525 using ValueType = Utf32StringType;
526 using CharType = Utf32StringType::value_type;
527 using StringTraits = SqlBasicStringOperations<Utf32StringType>;
529 static constexpr auto ColumnType = StringTraits::ColumnType;
531 static SQLRETURN InputParameter(SQLHSTMT stmt,
533 Utf32StringType
const& value,
534 SqlDataBinderCallback& cb)
noexcept
538 auto u16String = std::make_shared<std::u16string>(
ToUtf16(detail::SqlViewHelper<Utf32StringType>::View(value)));
539 cb.PlanPostExecuteCallback([u16String = u16String]() {});
540 auto const* data = u16String->data();
541 auto const charCount = u16String->size();
542 auto const sizeInBytes = u16String->size() *
sizeof(char16_t);
543 auto const CType = SQLSMALLINT { SQL_C_WCHAR };
544 auto const sqlType =
static_cast<SQLSMALLINT
>(charCount > SqlOptimalMaxColumnSize ? SQL_WLONGVARCHAR : SQL_WVARCHAR);
546 SQLLEN* indicator = cb.ProvideInputIndicator();
547 *indicator =
static_cast<SQLLEN
>(sizeInBytes);
549 return SQLBindParameter(stmt,
557 static_cast<SQLLEN
>(sizeInBytes),
561 static SQLRETURN OutputColumn(
562 SQLHSTMT stmt, SQLUSMALLINT column, Utf32StringType* result, SQLLEN* indicator, SqlDataBinderCallback& cb)
noexcept
564 return detail::BindOutputColumnNonUtf16Unicode<Utf32StringType>(stmt, column, result, indicator, cb);
567 static SQLRETURN GetColumn(SQLHSTMT stmt,
569 Utf32StringType* result,
571 SqlDataBinderCallback
const& cb)
noexcept
573 auto u16String = std::u16string {};
574 auto const sqlResult = detail::GetColumnUtf16(stmt, column, &u16String, indicator, cb);
575 if (!SQL_SUCCEEDED(sqlResult))
578 auto const u32String =
ToUtf32(u16String);
579 StringTraits::Resize(result,
static_cast<SQLLEN
>(u32String.size()));
580 std::copy_n((CharType
const*) u32String.data(), u32String.size(), StringTraits::Data(result));
585 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(Utf32StringType
const& value)
noexcept
587 auto u8String =
ToUtf8(detail::SqlViewHelper<Utf32StringType>::View(value));
588 return std::string(
reinterpret_cast<char const*
>(u8String.data()), u8String.size());
593template <
typename Utf8StringType>
594 requires SqlBasicStringBinderConcept<Utf8StringType, char8_t>
595struct SqlDataBinder<Utf8StringType>
597 using ValueType = Utf8StringType;
598 using CharType = char8_t;
599 using StringTraits = SqlBasicStringOperations<Utf8StringType>;
601 static constexpr auto ColumnType = StringTraits::ColumnType;
603 static SQLRETURN InputParameter(SQLHSTMT stmt,
605 Utf8StringType
const& value,
606 SqlDataBinderCallback& cb)
noexcept
610 auto u16String = std::make_shared<std::u16string>(
ToUtf16(detail::SqlViewHelper<Utf8StringType>::View(value)));
611 cb.PlanPostExecuteCallback([u16String = u16String]() {});
613 auto const CType = SQL_C_WCHAR;
614 auto const charCount = u16String->size();
615 auto const byteCount = u16String->size() *
sizeof(char16_t);
616 auto const sqlType =
static_cast<SQLSMALLINT
>(charCount > SqlOptimalMaxColumnSize ? SQL_WLONGVARCHAR : SQL_WVARCHAR);
618 SQLLEN* indicator = cb.ProvideInputIndicator();
619 *indicator =
static_cast<SQLLEN
>(byteCount);
621 return SQLBindParameter(stmt,
628 (SQLPOINTER) u16String->data(),
629 static_cast<SQLLEN
>(byteCount),
633 static SQLRETURN OutputColumn(
634 SQLHSTMT stmt, SQLUSMALLINT column, Utf8StringType* result, SQLLEN* indicator, SqlDataBinderCallback& cb)
noexcept
636 return detail::BindOutputColumnNonUtf16Unicode<Utf8StringType>(stmt, column, result, indicator, cb);
639 static SQLRETURN GetColumn(SQLHSTMT stmt,
641 Utf8StringType* result,
643 SqlDataBinderCallback
const& cb)
noexcept
645 auto u16String = std::u16string {};
646 u16String.resize(result->size());
647 auto const sqlReturn = detail::GetColumnUtf16(stmt, column, &u16String, indicator, cb);
648 if (SQL_SUCCEEDED(sqlReturn))
649 *result =
ToUtf8(u16String);
653 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(Utf8StringType
const& value)
noexcept
656 return std::string(
reinterpret_cast<char const*
>(value.data()), value.size());
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)