6#include "UnicodeConverter.hpp"
20constexpr 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 static 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(stmt, column, CType, bufferStart,
static_cast<SQLLEN
>(bufferCharsAvailable), indicator);
91 template <
typename Utf16StringType>
92 SQLRETURN GetColumnUtf16(SQLHSTMT stmt,
94 Utf16StringType* result,
96 SqlDataBinderCallback
const& )
noexcept
98 if constexpr (
requires { Utf16StringType::Capacity; })
99 result->resize(Utf16StringType::Capacity);
100 else if (result->size() == 0)
103 return GetRawColumnArrayData<SQL_C_WCHAR>(stmt, column, result, indicator);
106 template <
typename StringType>
107 SQLRETURN BindOutputColumnNonUtf16Unicode(
108 SQLHSTMT stmt, SQLUSMALLINT column, StringType* result, SQLLEN* indicator, SqlDataBinderCallback& cb)
noexcept
110 using CharType = StringType::value_type;
112 auto u16String = std::make_shared<std::u16string>();
113 if (!result->empty())
114 u16String->resize(result->size());
116 u16String->resize(255);
118 cb.PlanPostProcessOutputColumn([stmt, column, result, indicator, u16String = u16String]() {
119 if (*indicator == SQL_NULL_DATA)
121 else if (*indicator == SQL_NO_TOTAL)
123 else if (std::cmp_less_equal(*indicator, u16String->size() *
sizeof(
char16_t)))
124 u16String->resize(
static_cast<size_t>(*indicator) /
sizeof(
char16_t));
127 auto const totalCharsRequired =
static_cast<size_t>(*indicator) /
sizeof(char16_t);
128 *indicator +=
sizeof(char16_t);
129 u16String->resize(totalCharsRequired);
130 auto const sqlResult = SQLGetData(stmt, column, SQL_C_WCHAR, u16String->data(), *indicator, indicator);
132 assert(SQL_SUCCEEDED(sqlResult));
133 assert(std::cmp_equal(*indicator, totalCharsRequired *
sizeof(
char16_t)));
136 if constexpr (
sizeof(
typename StringType::value_type) == 1)
137 *result =
ToUtf8(*u16String);
138 else if constexpr (
sizeof(
typename StringType::value_type) == 4)
141 auto const u32String =
ToUtf32(*u16String);
142 *result = StringType {
143 (CharType
const*) u32String.data(),
144 (CharType
const*) u32String.data() + u32String.size(),
149 return SQLBindCol(stmt,
152 static_cast<SQLPOINTER
>(u16String->data()),
153 static_cast<SQLLEN
>(u16String->size() *
sizeof(
char16_t)),
160template <
typename AnsiStringType>
161 requires SqlBasicStringBinderConcept<AnsiStringType, char>
162struct SqlDataBinder<AnsiStringType>
164 using ValueType = AnsiStringType;
165 using CharType = char;
166 using StringTraits = SqlBasicStringOperations<AnsiStringType>;
168 static constexpr auto ColumnType = StringTraits::ColumnType;
170 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN InputParameter(SQLHSTMT stmt,
172 AnsiStringType
const& value,
173 SqlDataBinderCallback& cb)
noexcept
175 auto const charCount = StringTraits::Size(&value);
176 auto const sqlType =
static_cast<SQLSMALLINT
>(charCount > SqlOptimalMaxColumnSize ? SQL_LONGVARCHAR : SQL_VARCHAR);
178 SQLLEN* indicator = cb.ProvideInputIndicator();
179 *indicator =
static_cast<SQLLEN
>(charCount);
181 return SQLBindParameter(stmt,
188 (SQLPOINTER) StringTraits::Data(&value),
193 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN BatchInputParameter(SQLHSTMT stmt,
195 AnsiStringType
const* values,
197 SqlDataBinderCallback& cb)
noexcept
200 SQLLEN* indicators = cb.ProvideInputIndicators(rowCount);
201 for (
size_t i = 0; i < rowCount; ++i)
203 auto const len = StringTraits::Size(&values[i]);
204 indicators[i] =
static_cast<SQLLEN
>(len);
209 auto const sqlType =
static_cast<SQLSMALLINT
>(maxLen > SqlOptimalMaxColumnSize ? SQL_LONGVARCHAR : SQL_VARCHAR);
211 return SQLBindParameter(stmt,
219 sizeof(AnsiStringType),
223 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN OutputColumn(
224 SQLHSTMT stmt, SQLUSMALLINT column, AnsiStringType* result, SQLLEN* indicator, SqlDataBinderCallback& cb)
noexcept
226 if constexpr (
requires { AnsiStringType::Capacity; })
227 StringTraits::Resize(result, AnsiStringType::Capacity);
228 else if (StringTraits::Size(result) == 0)
229 StringTraits::Resize(result, 255);
231 if constexpr (
requires { StringTraits::PostProcessOutputColumn(result, *indicator); })
232 cb.PlanPostProcessOutputColumn(
233 [indicator, result]() { StringTraits::PostProcessOutputColumn(result, *indicator); });
235 cb.PlanPostProcessOutputColumn(
236 [stmt, column, indicator, result]() { PostProcessOutputColumn(stmt, column, result, indicator); });
238 return SQLBindCol(stmt,
241 (SQLPOINTER) StringTraits::Data(result),
242 (SQLLEN) StringTraits::Size(result),
246 static void PostProcessOutputColumn(SQLHSTMT stmt, SQLUSMALLINT column, AnsiStringType* result, SQLLEN* indicator)
250 if (*indicator == SQL_NO_TOTAL)
253 StringTraits::Resize(result,
static_cast<SQLLEN
>(StringTraits::Size(result)) - 1);
255 else if (*indicator == SQL_NULL_DATA)
258 StringTraits::Resize(result, 0);
260 else if (*indicator <=
static_cast<SQLLEN
>(StringTraits::Size(result)))
262 StringTraits::Resize(result, *indicator);
269 auto const totalCharsRequired = *indicator;
270 StringTraits::Resize(result, totalCharsRequired + 1);
271 auto const sqlResult =
272 SQLGetData(stmt, column, SQL_C_CHAR, StringTraits::Data(result), totalCharsRequired + 1, indicator);
274 assert(SQL_SUCCEEDED(sqlResult));
275 assert(*indicator == totalCharsRequired);
276 StringTraits::Resize(result, totalCharsRequired);
281 static SQLRETURN GetColumn(SQLHSTMT stmt,
283 AnsiStringType* result,
285 SqlDataBinderCallback
const& )
noexcept
287 if constexpr (
requires { AnsiStringType::Capacity; })
289 StringTraits::Resize(result, AnsiStringType::Capacity);
291 SQLGetData(stmt, column, SQL_C_CHAR, StringTraits::Data(result), AnsiStringType::Capacity, indicator);
292 if (rv == SQL_SUCCESS || rv == SQL_NO_DATA)
294 if (*indicator == SQL_NULL_DATA)
295 StringTraits::Resize(result, 0);
296 else if (*indicator != SQL_NO_TOTAL)
297 StringTraits::Resize(
298 result,
static_cast<SQLLEN
>((std::min) (AnsiStringType::Capacity,
static_cast<size_t>(*indicator))));
300 if constexpr (
requires { StringTraits::PostProcessOutputColumn(result, *indicator); })
301 StringTraits::PostProcessOutputColumn(result, *indicator);
306 StringTraits::Reserve(result, 15);
307 size_t writeIndex = 0;
311 auto*
const bufferStart = StringTraits::Data(result) + writeIndex;
312 size_t const bufferSize = StringTraits::Size(result) - writeIndex;
314 SQLGetData(stmt, column, SQL_C_CHAR, bufferStart,
static_cast<SQLLEN
>(bufferSize), indicator);
320 if (*indicator != SQL_NULL_DATA)
322 StringTraits::Resize(result,
static_cast<SQLLEN
>(writeIndex) + *indicator);
323 *indicator =
static_cast<SQLLEN
>(StringTraits::Size(result));
326 case SQL_SUCCESS_WITH_INFO: {
328 if (*indicator == SQL_NO_TOTAL)
331 writeIndex += bufferSize - 1;
332 StringTraits::Resize(result,
static_cast<SQLLEN
>((2 * writeIndex) + 1));
334 else if (std::cmp_greater_equal(*indicator, bufferSize))
337 writeIndex += bufferSize - 1;
338 StringTraits::Resize(result,
static_cast<SQLLEN
>(writeIndex) + *indicator);
343 StringTraits::Resize(result,
static_cast<SQLLEN
>(writeIndex) + *indicator - 1);
349 if constexpr (
requires { StringTraits::PostProcessOutputColumn(result, *indicator); })
350 StringTraits::PostProcessOutputColumn(result, *indicator);
357 static LIGHTWEIGHT_FORCE_INLINE std::string_view Inspect(AnsiStringType
const& value)
noexcept
359 return { StringTraits::Data(&value), StringTraits::Size(&value) };
364template <
typename Utf16StringType>
365 requires(SqlBasicStringBinderConcept<Utf16StringType, char16_t>
366 || (SqlBasicStringBinderConcept<Utf16StringType, unsigned short>)
367 || (SqlBasicStringBinderConcept<Utf16StringType, wchar_t> &&
sizeof(
wchar_t) == 2))
368struct SqlDataBinder<Utf16StringType>
370 using ValueType = Utf16StringType;
371 using CharType = std::remove_cvref_t<decltype(std::declval<Utf16StringType>()[0])>;
372 using StringTraits = SqlBasicStringOperations<Utf16StringType>;
374 static constexpr auto ColumnType = StringTraits::ColumnType;
376 static constexpr auto CType = SQL_C_WCHAR;
378 static SQLRETURN InputParameter(SQLHSTMT stmt,
380 Utf16StringType
const& value,
381 SqlDataBinderCallback& cb)
noexcept
383 switch (cb.ServerType())
385 case SqlServerType::POSTGRESQL: {
387 auto u8String = std::make_shared<std::u8string>(
ToUtf8(detail::SqlViewHelper<Utf16StringType>::View(value)));
388 cb.PlanPostExecuteCallback([u8String = u8String]() {});
390 SQLLEN* indicator = cb.ProvideInputIndicator();
391 *indicator =
static_cast<SQLLEN
>(u8String->size());
393 return SQLBindParameter(stmt,
400 (SQLPOINTER) u8String->data(),
404 case SqlServerType::MYSQL:
405 case SqlServerType::SQLITE:
406 case SqlServerType::MICROSOFT_SQL:
407 case SqlServerType::UNKNOWN: {
408 using CharType = StringTraits::CharType;
409 auto const* data = StringTraits::Data(&value);
410 auto const sizeInBytes = StringTraits::Size(&value) *
sizeof(CharType);
411 auto const charCount = StringTraits::Size(&value);
413 static_cast<SQLSMALLINT
>(charCount > SqlOptimalMaxColumnSize ? SQL_WLONGVARCHAR : SQL_WVARCHAR);
415 SQLLEN* indicator = cb.ProvideInputIndicator();
416 *indicator =
static_cast<SQLLEN
>(sizeInBytes);
418 return SQLBindParameter(stmt,
426 static_cast<SQLLEN
>(sizeInBytes),
433 static SQLRETURN OutputColumn(
434 SQLHSTMT stmt, SQLUSMALLINT column, Utf16StringType* result, SQLLEN* indicator, SqlDataBinderCallback& cb)
noexcept
436 if constexpr (
requires { Utf16StringType::Capacity; })
437 StringTraits::Resize(result, Utf16StringType::Capacity);
438 else if (StringTraits::Size(result) == 0)
439 StringTraits::Resize(result, 255);
441 if constexpr (
requires { StringTraits::PostProcessOutputColumn(result, *indicator); })
443 cb.PlanPostProcessOutputColumn(
444 [indicator, result]() { StringTraits::PostProcessOutputColumn(result, *indicator); });
448 cb.PlanPostProcessOutputColumn([stmt, column, indicator, result]() {
451 if (*indicator == SQL_NULL_DATA)
452 StringTraits::Resize(result, 0);
453 else if (*indicator == SQL_NO_TOTAL)
455 else if (*indicator <=
static_cast<SQLLEN
>(result->size() *
sizeof(
char16_t)))
456 result->resize(
static_cast<size_t>(*indicator) /
sizeof(
char16_t));
459 auto const totalCharsRequired =
static_cast<size_t>(*indicator) /
sizeof(char16_t);
460 *indicator +=
sizeof(char16_t);
461 result->resize(totalCharsRequired);
462 auto const sqlResult = SQLGetData(stmt, column, SQL_C_WCHAR, result->data(), *indicator, indicator);
464 assert(SQL_SUCCEEDED(sqlResult));
465 assert(std::cmp_equal(*indicator, totalCharsRequired *
sizeof(
char16_t)));
470 stmt, column, CType, (SQLPOINTER) StringTraits::Data(result), (SQLLEN) StringTraits::Size(result), indicator);
473 static SQLRETURN GetColumn(SQLHSTMT stmt,
475 Utf16StringType* result,
477 SqlDataBinderCallback
const& cb)
noexcept
479 return detail::GetColumnUtf16(stmt, column, result, indicator, cb);
482 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(Utf16StringType
const& value)
noexcept
484 auto u8String =
ToUtf8(detail::SqlViewHelper<Utf16StringType>::View(value));
485 return std::string(
reinterpret_cast<char const*
>(u8String.data()), u8String.size());
490template <
typename Utf32StringType>
491 requires(SqlBasicStringBinderConcept<Utf32StringType, char32_t>
492 || (SqlBasicStringBinderConcept<Utf32StringType, uint32_t>)
493 || (SqlBasicStringBinderConcept<Utf32StringType, wchar_t> &&
sizeof(
wchar_t) == 4))
494struct SqlDataBinder<Utf32StringType>
496 using ValueType = Utf32StringType;
497 using CharType = Utf32StringType::value_type;
498 using StringTraits = SqlBasicStringOperations<Utf32StringType>;
500 static constexpr auto ColumnType = StringTraits::ColumnType;
502 static SQLRETURN InputParameter(SQLHSTMT stmt,
504 Utf32StringType
const& value,
505 SqlDataBinderCallback& cb)
noexcept
507 switch (cb.ServerType())
509 case SqlServerType::POSTGRESQL: {
511 auto u8String = std::make_shared<std::u8string>(
ToUtf8(detail::SqlViewHelper<Utf32StringType>::View(value)));
512 cb.PlanPostExecuteCallback([u8String = u8String]() {});
514 SQLLEN* indicator = cb.ProvideInputIndicator();
515 *indicator =
static_cast<SQLLEN
>(u8String->size());
517 return SQLBindParameter(stmt,
524 (SQLPOINTER) u8String->data(),
528 case SqlServerType::MYSQL:
529 case SqlServerType::SQLITE:
530 case SqlServerType::MICROSOFT_SQL:
531 case SqlServerType::UNKNOWN: {
533 std::make_shared<std::u16string>(
ToUtf16(detail::SqlViewHelper<Utf32StringType>::View(value)));
534 cb.PlanPostExecuteCallback([u8String = u16String]() {});
535 auto const* data = u16String->data();
536 auto const charCount = u16String->size();
537 auto const sizeInBytes = u16String->size() *
sizeof(char16_t);
538 auto const CType = SQLSMALLINT { SQL_C_WCHAR };
540 static_cast<SQLSMALLINT
>(charCount > SqlOptimalMaxColumnSize ? SQL_WLONGVARCHAR : SQL_WVARCHAR);
542 SQLLEN* indicator = cb.ProvideInputIndicator();
543 *indicator =
static_cast<SQLLEN
>(sizeInBytes);
545 return SQLBindParameter(stmt,
553 static_cast<SQLLEN
>(sizeInBytes),
560 static SQLRETURN OutputColumn(
561 SQLHSTMT stmt, SQLUSMALLINT column, Utf32StringType* result, SQLLEN* indicator, SqlDataBinderCallback& cb)
noexcept
563 return detail::BindOutputColumnNonUtf16Unicode<Utf32StringType>(stmt, column, result, indicator, cb);
566 static SQLRETURN GetColumn(SQLHSTMT stmt,
568 Utf32StringType* result,
570 SqlDataBinderCallback
const& cb)
noexcept
572 auto u16String = std::u16string {};
573 auto const sqlResult = detail::GetColumnUtf16(stmt, column, &u16String, indicator, cb);
574 if (!SQL_SUCCEEDED(sqlResult))
577 auto const u32String =
ToUtf32(u16String);
578 StringTraits::Resize(result,
static_cast<SQLLEN
>(u32String.size()));
579 std::copy_n((CharType
const*) u32String.data(), u32String.size(), StringTraits::Data(result));
584 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(Utf32StringType
const& value)
noexcept
586 auto u8String =
ToUtf8(detail::SqlViewHelper<Utf32StringType>::View(value));
587 return std::string(
reinterpret_cast<char const*
>(u8String.data()), u8String.size());
592template <
typename Utf8StringType>
593 requires SqlBasicStringBinderConcept<Utf8StringType, char8_t>
594struct SqlDataBinder<Utf8StringType>
596 using ValueType = Utf8StringType;
597 using CharType = char8_t;
598 using StringTraits = SqlBasicStringOperations<Utf8StringType>;
600 static constexpr auto ColumnType = StringTraits::ColumnType;
602 static SQLRETURN InputParameter(SQLHSTMT stmt,
604 Utf8StringType
const& value,
605 SqlDataBinderCallback& cb)
noexcept
607 switch (cb.ServerType())
609 case SqlServerType::POSTGRESQL: {
611 auto const len = value.size();
612 SQLLEN* indicator = cb.ProvideInputIndicator();
613 *indicator =
static_cast<SQLLEN
>(len);
615 return SQLBindParameter(
616 stmt, column, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, len, 0, (SQLPOINTER) value.data(), 0, indicator);
618 case SqlServerType::MYSQL:
619 case SqlServerType::SQLITE:
620 case SqlServerType::MICROSOFT_SQL:
621 case SqlServerType::UNKNOWN: {
623 std::make_shared<std::u16string>(
ToUtf16(detail::SqlViewHelper<Utf8StringType>::View(value)));
624 cb.PlanPostExecuteCallback([u16String = u16String]() {});
626 auto const CType = SQL_C_WCHAR;
627 auto const charCount = u16String->size();
628 auto const byteCount = u16String->size() *
sizeof(char16_t);
630 static_cast<SQLSMALLINT
>(charCount > SqlOptimalMaxColumnSize ? SQL_WLONGVARCHAR : SQL_WVARCHAR);
632 SQLLEN* indicator = cb.ProvideInputIndicator();
633 *indicator =
static_cast<SQLLEN
>(byteCount);
635 return SQLBindParameter(stmt,
642 (SQLPOINTER) u16String->data(),
643 static_cast<SQLLEN
>(byteCount),
650 static SQLRETURN OutputColumn(
651 SQLHSTMT stmt, SQLUSMALLINT column, Utf8StringType* result, SQLLEN* indicator, SqlDataBinderCallback& cb)
noexcept
653 return detail::BindOutputColumnNonUtf16Unicode<Utf8StringType>(stmt, column, result, indicator, cb);
656 static SQLRETURN GetColumn(SQLHSTMT stmt,
658 Utf8StringType* result,
660 SqlDataBinderCallback
const& cb)
noexcept
662 auto u16String = std::u16string {};
663 u16String.resize(result->size());
664 auto const sqlReturn = detail::GetColumnUtf16(stmt, column, &u16String, indicator, cb);
665 if (SQL_SUCCEEDED(sqlReturn))
666 *result =
ToUtf8(u16String);
670 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(Utf8StringType
const& value)
noexcept
673 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)