6#include "UnicodeConverter.hpp"
16constexpr std::size_t SqlOptimalMaxColumnSize = 4000;
21template <auto CType,
typename ArrayType>
22static SQLRETURN GetArrayData(SQLHSTMT stmt, SQLUSMALLINT column, ArrayType* result, SQLLEN* indicator)
noexcept
24 using CharType =
typename ArrayType::value_type;
30 auto sqlResult = SQLGetData(
31 stmt, column, CType, (SQLPOINTER) result->data(), (SQLLEN) (result->size() *
sizeof(CharType)), indicator);
33 if (sqlResult == SQL_SUCCESS || sqlResult == SQL_NO_DATA)
36 if (*indicator == SQL_NULL_DATA)
39 result->resize(*indicator /
sizeof(CharType));
43 if (sqlResult == SQL_SUCCESS_WITH_INFO && *indicator >
static_cast<SQLLEN
>(result->size()))
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)));
57 size_t writeIndex = 0;
58 while (sqlResult == SQL_SUCCESS_WITH_INFO && *indicator == SQL_NO_TOTAL)
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);
70template <
typename Utf16StringType>
71SQLRETURN GetColumnUtf16(SQLHSTMT stmt,
73 Utf16StringType* result,
77 if constexpr (
requires { Utf16StringType::Capacity; })
78 result->resize(Utf16StringType::Capacity);
79 else if (result->size() == 0)
82 return GetArrayData<SQL_C_WCHAR>(stmt, column, result, indicator);
85template <
typename StringType>
86SQLRETURN OutputColumnNonUtf16Unicode(
87 SQLHSTMT stmt, SQLUSMALLINT column, StringType* result, SQLLEN* indicator,
SqlDataBinderCallback& cb)
noexcept
89 using CharType =
typename StringType::value_type;
91 auto u16String = std::make_shared<std::u16string>();
93 u16String->resize(result->size());
95 u16String->resize(255);
97 cb.PlanPostProcessOutputColumn([stmt, column, result, indicator, u16String = u16String]() {
98 if (*indicator == SQL_NULL_DATA)
100 else if (*indicator == SQL_NO_TOTAL)
102 else if (*indicator <=
static_cast<SQLLEN
>(u16String->size() *
sizeof(
char16_t)))
103 u16String->resize(*indicator /
sizeof(
char16_t));
106 auto const totalCharsRequired =
static_cast<SQLLEN
>(*indicator /
sizeof(char16_t));
107 *indicator +=
sizeof(char16_t);
108 u16String->resize(totalCharsRequired);
109 auto const sqlResult = SQLGetData(stmt, column, SQL_C_WCHAR, u16String->data(), *indicator, indicator);
111 assert(SQL_SUCCEEDED(sqlResult));
112 assert(*indicator ==
static_cast<SQLLEN
>(totalCharsRequired *
sizeof(
char16_t)));
115 if constexpr (
sizeof(
typename StringType::value_type) == 1)
116 *result =
ToUtf8(*u16String);
117 else if constexpr (
sizeof(
typename StringType::value_type) == 4)
120 auto const u32String =
ToUtf32(*u16String);
121 *result = StringType {
122 (CharType
const*) u32String.data(),
123 (CharType
const*) u32String.data() + u32String.size(),
128 return SQLBindCol(stmt,
131 static_cast<SQLPOINTER
>(u16String->data()),
132 static_cast<SQLLEN
>(u16String->size() *
sizeof(
char16_t)),
139template <
typename AnsiStringType>
140 requires SqlBasicStringBinderConcept<AnsiStringType, char>
141struct SqlDataBinder<AnsiStringType>
143 using ValueType = AnsiStringType;
144 using CharType = char;
145 using StringTraits = SqlBasicStringOperations<AnsiStringType>;
147 static constexpr auto ColumnType = StringTraits::ColumnType;
149 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN InputParameter(SQLHSTMT stmt,
151 AnsiStringType
const& value,
154 return SQLBindParameter(stmt,
159 StringTraits::Size(&value),
161 (SQLPOINTER) StringTraits::Data(&value),
162 sizeof(AnsiStringType),
166 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN OutputColumn(SQLHSTMT stmt,
168 AnsiStringType* result,
172 if constexpr (
requires { AnsiStringType::Capacity; })
173 StringTraits::Resize(result, AnsiStringType::Capacity);
174 else if (StringTraits::Size(result) == 0)
175 StringTraits::Resize(result, 255);
177 if constexpr (
requires { StringTraits::PostProcessOutputColumn(result, *indicator); })
178 cb.PlanPostProcessOutputColumn(
179 [indicator, result]() { StringTraits::PostProcessOutputColumn(result, *indicator); });
181 cb.PlanPostProcessOutputColumn(
182 [stmt, column, indicator, result]() { PostProcessOutputColumn(stmt, column, result, indicator); });
184 return SQLBindCol(stmt,
187 (SQLPOINTER) StringTraits::Data(result),
188 (SQLLEN) StringTraits::Size(result),
192 static void PostProcessOutputColumn(SQLHSTMT stmt, SQLUSMALLINT column, AnsiStringType* result, SQLLEN* indicator)
196 if (*indicator == SQL_NO_TOTAL)
199 StringTraits::Resize(result, StringTraits::Size(result) - 1);
201 else if (*indicator == SQL_NULL_DATA)
204 StringTraits::Resize(result, 0);
206 else if (*indicator <=
static_cast<SQLLEN
>(StringTraits::Size(result)))
208 StringTraits::Resize(result,
static_cast<size_t>(*indicator));
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);
220 assert(SQL_SUCCEEDED(sqlResult));
221 assert(*indicator == totalCharsRequired);
222 StringTraits::Resize(result, totalCharsRequired);
227 static SQLRETURN GetColumn(SQLHSTMT stmt,
229 AnsiStringType* result,
233 if constexpr (
requires { AnsiStringType::Capacity; })
235 StringTraits::Resize(result, AnsiStringType::Capacity);
237 SQLGetData(stmt, column, SQL_C_CHAR, StringTraits::Data(result), AnsiStringType::Capacity, indicator);
238 if (rv == SQL_SUCCESS || rv == SQL_NO_DATA)
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)));
245 if constexpr (
requires { StringTraits::PostProcessOutputColumn(result, *indicator); })
246 StringTraits::PostProcessOutputColumn(result, *indicator);
251 StringTraits::Reserve(result, 15);
252 size_t writeIndex = 0;
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);
264 if (*indicator != SQL_NULL_DATA)
266 StringTraits::Resize(result, writeIndex + *indicator);
267 *indicator = StringTraits::Size(result);
270 case SQL_SUCCESS_WITH_INFO: {
272 if (*indicator == SQL_NO_TOTAL)
275 writeIndex += bufferSize - 1;
276 StringTraits::Resize(result, (2 * writeIndex) + 1);
278 else if (std::cmp_greater_equal(*indicator, bufferSize))
281 writeIndex += bufferSize - 1;
282 StringTraits::Resize(result, writeIndex + *indicator);
287 StringTraits::Resize(result, writeIndex + *indicator - 1);
293 if constexpr (
requires { StringTraits::PostProcessOutputColumn(result, *indicator); })
294 StringTraits::PostProcessOutputColumn(result, *indicator);
301 static LIGHTWEIGHT_FORCE_INLINE std::string_view Inspect(AnsiStringType
const& value)
noexcept
303 return { StringTraits::Data(&value), StringTraits::Size(&value) };
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>
314 using ValueType = Utf16StringType;
315 using CharType = std::remove_cvref_t<decltype(std::declval<Utf16StringType>()[0])>;
316 using StringTraits = SqlBasicStringOperations<Utf16StringType>;
318 static constexpr auto ColumnType = StringTraits::ColumnType;
320 static constexpr auto CType = SQL_C_WCHAR;
322 static SQLRETURN InputParameter(SQLHSTMT stmt,
324 Utf16StringType
const& value,
327 switch (cb.ServerType())
329 case SqlServerType::POSTGRESQL: {
332 std::make_shared<std::u8string>(
ToUtf8(detail::SqlViewHelper<Utf16StringType>::View(value)));
333 cb.PlanPostExecuteCallback([u8String = u8String]() {});
334 return SQLBindParameter(stmt,
341 (SQLPOINTER) u8String->data(),
345 case SqlServerType::ORACLE:
346 case SqlServerType::MYSQL:
347 case SqlServerType::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);
355 static_cast<SQLSMALLINT
>(charCount > SqlOptimalMaxColumnSize ? SQL_WLONGVARCHAR : SQL_WVARCHAR);
356 return SQLBindParameter(stmt,
371 static SQLRETURN OutputColumn(SQLHSTMT stmt,
373 Utf16StringType* result,
377 if constexpr (
requires { Utf16StringType::Capacity; })
378 StringTraits::Resize(result, Utf16StringType::Capacity);
379 else if (StringTraits::Size(result) == 0)
380 StringTraits::Resize(result, 255);
382 if constexpr (
requires { StringTraits::PostProcessOutputColumn(result, *indicator); })
384 cb.PlanPostProcessOutputColumn(
385 [indicator, result]() { StringTraits::PostProcessOutputColumn(result, *indicator); });
389 cb.PlanPostProcessOutputColumn([stmt, column, indicator, result]() {
392 if (*indicator == SQL_NULL_DATA)
393 StringTraits::Resize(result, 0);
394 else if (*indicator == SQL_NO_TOTAL)
396 else if (*indicator <=
static_cast<SQLLEN
>(result->size() *
sizeof(
char16_t)))
397 result->resize(*indicator /
sizeof(
char16_t));
400 auto const totalCharsRequired =
static_cast<SQLLEN
>(*indicator /
sizeof(char16_t));
401 *indicator +=
sizeof(char16_t);
402 result->resize(totalCharsRequired);
403 auto const sqlResult = SQLGetData(stmt, column, SQL_C_WCHAR, result->data(), *indicator, indicator);
405 assert(SQL_SUCCEEDED(sqlResult));
406 assert(*indicator ==
static_cast<SQLLEN
>(totalCharsRequired *
sizeof(
char16_t)));
410 return SQLBindCol(stmt,
413 (SQLPOINTER) StringTraits::Data(result),
414 (SQLLEN) StringTraits::Size(result),
418 static SQLRETURN GetColumn(SQLHSTMT stmt,
420 Utf16StringType* result,
424 return detail::GetColumnUtf16(stmt, column, result, indicator, cb);
427 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(Utf16StringType
const& value)
noexcept
429 auto u8String =
ToUtf8(detail::SqlViewHelper<Utf16StringType>::View(value));
430 return std::string(
reinterpret_cast<char const*
>(u8String.data()), u8String.size());
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>
441 using ValueType = Utf32StringType;
442 using CharType =
typename Utf32StringType::value_type;
443 using StringTraits = SqlBasicStringOperations<Utf32StringType>;
445 static constexpr auto ColumnType = StringTraits::ColumnType;
447 static SQLRETURN InputParameter(SQLHSTMT stmt,
449 Utf32StringType
const& value,
452 switch (cb.ServerType())
454 case SqlServerType::POSTGRESQL: {
457 std::make_shared<std::u8string>(
ToUtf8(detail::SqlViewHelper<Utf32StringType>::View(value)));
458 cb.PlanPostExecuteCallback([u8String = u8String]() {});
459 return SQLBindParameter(stmt,
466 (SQLPOINTER) u8String->data(),
470 case SqlServerType::ORACLE:
471 case SqlServerType::MYSQL:
472 case SqlServerType::SQLITE:
473 case SqlServerType::MICROSOFT_SQL:
474 case SqlServerType::UNKNOWN: {
476 std::make_shared<std::u16string>(
ToUtf16(detail::SqlViewHelper<Utf32StringType>::View(value)));
477 cb.PlanPostExecuteCallback([u8String = u16String]() {});
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,
498 static SQLRETURN OutputColumn(SQLHSTMT stmt,
500 Utf32StringType* result,
504 return detail::OutputColumnNonUtf16Unicode<Utf32StringType>(stmt, column, result, indicator, cb);
507 static SQLRETURN GetColumn(SQLHSTMT stmt,
509 Utf32StringType* result,
513 auto u16String = std::u16string {};
514 auto const sqlResult = detail::GetColumnUtf16(stmt, column, &u16String, indicator, cb);
515 if (!SQL_SUCCEEDED(sqlResult))
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));
525 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(Utf32StringType
const& value)
noexcept
527 auto u8String =
ToUtf8(detail::SqlViewHelper<Utf32StringType>::View(value));
528 return std::string(
reinterpret_cast<char const*
>(u8String.data()), u8String.size());
533template <
typename Utf8StringType>
534 requires SqlBasicStringBinderConcept<Utf8StringType, char8_t>
535struct SqlDataBinder<Utf8StringType>
537 using ValueType = Utf8StringType;
538 using CharType = char8_t;
539 using StringTraits = SqlBasicStringOperations<Utf8StringType>;
541 static constexpr auto ColumnType = StringTraits::ColumnType;
543 static SQLRETURN InputParameter(SQLHSTMT stmt,
545 Utf8StringType
const& value,
548 switch (cb.ServerType())
550 case SqlServerType::POSTGRESQL: {
552 return SQLBindParameter(stmt,
559 (SQLPOINTER) value.data(),
563 case SqlServerType::ORACLE:
564 case SqlServerType::MYSQL:
565 case SqlServerType::SQLITE:
566 case SqlServerType::MICROSOFT_SQL:
567 case SqlServerType::UNKNOWN: {
569 std::make_shared<std::u16string>(
ToUtf16(detail::SqlViewHelper<Utf8StringType>::View(value)));
570 cb.PlanPostExecuteCallback([u16String = u16String]() {});
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,
583 (SQLPOINTER) u16String->data(),
591 static SQLRETURN OutputColumn(SQLHSTMT stmt,
593 Utf8StringType* result,
597 return detail::OutputColumnNonUtf16Unicode<Utf8StringType>(stmt, column, result, indicator, cb);
600 static SQLRETURN GetColumn(SQLHSTMT stmt,
602 Utf8StringType* result,
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);
614 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(Utf8StringType
const& value)
noexcept
617 return std::string(
reinterpret_cast<char const*
>(value.data()), value.size());
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)