6#include "UnicodeConverter.hpp"
19constexpr std::size_t SqlOptimalMaxColumnSize = 4000;
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>()) };
37 static SQLRETURN GetRawColumnArrayData(SQLHSTMT stmt, SQLUSMALLINT column, ArrayType* result, SQLLEN* indicator)
noexcept
39 using CharType =
typename ArrayType::value_type;
45 auto sqlResult = SQLGetData(stmt,
48 static_cast<SQLPOINTER
>(result->data()),
49 static_cast<SQLLEN
>(result->size() *
sizeof(CharType)),
52 if (sqlResult == SQL_SUCCESS || sqlResult == SQL_NO_DATA)
55 if (*indicator == SQL_NULL_DATA)
58 result->resize(*indicator /
sizeof(CharType));
62 if (sqlResult == SQL_SUCCESS_WITH_INFO && *indicator >
static_cast<SQLLEN
>(result->size()))
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)));
76 size_t writeIndex = 0;
77 while (sqlResult == SQL_SUCCESS_WITH_INFO && *indicator == SQL_NO_TOTAL)
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);
89 template <
typename Utf16StringType>
90 SQLRETURN GetColumnUtf16(SQLHSTMT stmt,
92 Utf16StringType* result,
94 SqlDataBinderCallback
const& )
noexcept
96 if constexpr (
requires { Utf16StringType::Capacity; })
97 result->resize(Utf16StringType::Capacity);
98 else if (result->size() == 0)
101 return GetRawColumnArrayData<SQL_C_WCHAR>(stmt, column, result, indicator);
104 template <
typename StringType>
105 SQLRETURN BindOutputColumnNonUtf16Unicode(
106 SQLHSTMT stmt, SQLUSMALLINT column, StringType* result, SQLLEN* indicator, SqlDataBinderCallback& cb)
noexcept
108 using CharType =
typename StringType::value_type;
110 auto u16String = std::make_shared<std::u16string>();
111 if (!result->empty())
112 u16String->resize(result->size());
114 u16String->resize(255);
116 cb.PlanPostProcessOutputColumn([stmt, column, result, indicator, u16String = u16String]() {
117 if (*indicator == SQL_NULL_DATA)
119 else if (*indicator == SQL_NO_TOTAL)
121 else if (std::cmp_less_equal(*indicator, u16String->size() *
sizeof(
char16_t)))
122 u16String->resize(*indicator /
sizeof(
char16_t));
125 auto const totalCharsRequired =
static_cast<SQLLEN
>(*indicator /
sizeof(char16_t));
126 *indicator +=
sizeof(char16_t);
127 u16String->resize(totalCharsRequired);
128 auto const sqlResult = SQLGetData(stmt, column, SQL_C_WCHAR, u16String->data(), *indicator, indicator);
130 assert(SQL_SUCCEEDED(sqlResult));
131 assert(std::cmp_equal(*indicator, totalCharsRequired *
sizeof(
char16_t)));
134 if constexpr (
sizeof(
typename StringType::value_type) == 1)
135 *result =
ToUtf8(*u16String);
136 else if constexpr (
sizeof(
typename StringType::value_type) == 4)
139 auto const u32String =
ToUtf32(*u16String);
140 *result = StringType {
141 (CharType
const*) u32String.data(),
142 (CharType
const*) u32String.data() + u32String.size(),
147 return SQLBindCol(stmt,
150 static_cast<SQLPOINTER
>(u16String->data()),
151 static_cast<SQLLEN
>(u16String->size() *
sizeof(
char16_t)),
158template <
typename AnsiStringType>
159 requires SqlBasicStringBinderConcept<AnsiStringType, char>
160struct SqlDataBinder<AnsiStringType>
162 using ValueType = AnsiStringType;
163 using CharType = char;
164 using StringTraits = SqlBasicStringOperations<AnsiStringType>;
166 static constexpr auto ColumnType = StringTraits::ColumnType;
168 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN InputParameter(SQLHSTMT stmt,
170 AnsiStringType
const& value,
171 SqlDataBinderCallback& )
noexcept
173 auto const charCount = StringTraits::Size(&value);
174 auto const sqlType =
static_cast<SQLSMALLINT
>(charCount > SqlOptimalMaxColumnSize ? SQL_LONGVARCHAR : SQL_VARCHAR);
175 return SQLBindParameter(stmt,
182 (SQLPOINTER) StringTraits::Data(&value),
183 sizeof(AnsiStringType),
187 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN OutputColumn(
188 SQLHSTMT stmt, SQLUSMALLINT column, AnsiStringType* result, SQLLEN* indicator, SqlDataBinderCallback& cb)
noexcept
190 if constexpr (
requires { AnsiStringType::Capacity; })
191 StringTraits::Resize(result, AnsiStringType::Capacity);
192 else if (StringTraits::Size(result) == 0)
193 StringTraits::Resize(result, 255);
195 if constexpr (
requires { StringTraits::PostProcessOutputColumn(result, *indicator); })
196 cb.PlanPostProcessOutputColumn(
197 [indicator, result]() { StringTraits::PostProcessOutputColumn(result, *indicator); });
199 cb.PlanPostProcessOutputColumn(
200 [stmt, column, indicator, result]() { PostProcessOutputColumn(stmt, column, result, indicator); });
202 return SQLBindCol(stmt,
205 (SQLPOINTER) StringTraits::Data(result),
206 (SQLLEN) StringTraits::Size(result),
210 static void PostProcessOutputColumn(SQLHSTMT stmt, SQLUSMALLINT column, AnsiStringType* result, SQLLEN* indicator)
214 if (*indicator == SQL_NO_TOTAL)
217 StringTraits::Resize(result, StringTraits::Size(result) - 1);
219 else if (*indicator == SQL_NULL_DATA)
222 StringTraits::Resize(result, 0);
224 else if (*indicator <=
static_cast<SQLLEN
>(StringTraits::Size(result)))
226 StringTraits::Resize(result,
static_cast<size_t>(*indicator));
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);
238 assert(SQL_SUCCEEDED(sqlResult));
239 assert(*indicator == totalCharsRequired);
240 StringTraits::Resize(result, totalCharsRequired);
245 static SQLRETURN GetColumn(SQLHSTMT stmt,
247 AnsiStringType* result,
249 SqlDataBinderCallback
const& )
noexcept
251 if constexpr (
requires { AnsiStringType::Capacity; })
253 StringTraits::Resize(result, AnsiStringType::Capacity);
255 SQLGetData(stmt, column, SQL_C_CHAR, StringTraits::Data(result), AnsiStringType::Capacity, indicator);
256 if (rv == SQL_SUCCESS || rv == SQL_NO_DATA)
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)));
263 if constexpr (
requires { StringTraits::PostProcessOutputColumn(result, *indicator); })
264 StringTraits::PostProcessOutputColumn(result, *indicator);
269 StringTraits::Reserve(result, 15);
270 size_t writeIndex = 0;
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);
282 if (*indicator != SQL_NULL_DATA)
284 StringTraits::Resize(result, writeIndex + *indicator);
285 *indicator = StringTraits::Size(result);
288 case SQL_SUCCESS_WITH_INFO: {
290 if (*indicator == SQL_NO_TOTAL)
293 writeIndex += bufferSize - 1;
294 StringTraits::Resize(result, (2 * writeIndex) + 1);
296 else if (std::cmp_greater_equal(*indicator, bufferSize))
299 writeIndex += bufferSize - 1;
300 StringTraits::Resize(result, writeIndex + *indicator);
305 StringTraits::Resize(result, writeIndex + *indicator - 1);
311 if constexpr (
requires { StringTraits::PostProcessOutputColumn(result, *indicator); })
312 StringTraits::PostProcessOutputColumn(result, *indicator);
319 static LIGHTWEIGHT_FORCE_INLINE std::string_view Inspect(AnsiStringType
const& value)
noexcept
321 return { StringTraits::Data(&value), StringTraits::Size(&value) };
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>
332 using ValueType = Utf16StringType;
333 using CharType = std::remove_cvref_t<decltype(std::declval<Utf16StringType>()[0])>;
334 using StringTraits = SqlBasicStringOperations<Utf16StringType>;
336 static constexpr auto ColumnType = StringTraits::ColumnType;
338 static constexpr auto CType = SQL_C_WCHAR;
340 static SQLRETURN InputParameter(SQLHSTMT stmt,
342 Utf16StringType
const& value,
343 SqlDataBinderCallback& cb)
noexcept
345 switch (cb.ServerType())
347 case SqlServerType::POSTGRESQL: {
349 auto u8String = std::make_shared<std::u8string>(
ToUtf8(detail::SqlViewHelper<Utf16StringType>::View(value)));
350 cb.PlanPostExecuteCallback([u8String = u8String]() {});
351 return SQLBindParameter(stmt,
358 (SQLPOINTER) u8String->data(),
362 case SqlServerType::ORACLE:
363 case SqlServerType::MYSQL:
364 case SqlServerType::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);
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);
380 static SQLRETURN OutputColumn(
381 SQLHSTMT stmt, SQLUSMALLINT column, Utf16StringType* result, SQLLEN* indicator, SqlDataBinderCallback& cb)
noexcept
383 if constexpr (
requires { Utf16StringType::Capacity; })
384 StringTraits::Resize(result, Utf16StringType::Capacity);
385 else if (StringTraits::Size(result) == 0)
386 StringTraits::Resize(result, 255);
388 if constexpr (
requires { StringTraits::PostProcessOutputColumn(result, *indicator); })
390 cb.PlanPostProcessOutputColumn(
391 [indicator, result]() { StringTraits::PostProcessOutputColumn(result, *indicator); });
395 cb.PlanPostProcessOutputColumn([stmt, column, indicator, result]() {
398 if (*indicator == SQL_NULL_DATA)
399 StringTraits::Resize(result, 0);
400 else if (*indicator == SQL_NO_TOTAL)
402 else if (*indicator <=
static_cast<SQLLEN
>(result->size() *
sizeof(
char16_t)))
403 result->resize(*indicator /
sizeof(
char16_t));
406 auto const totalCharsRequired =
static_cast<SQLLEN
>(*indicator /
sizeof(char16_t));
407 *indicator +=
sizeof(char16_t);
408 result->resize(totalCharsRequired);
409 auto const sqlResult = SQLGetData(stmt, column, SQL_C_WCHAR, result->data(), *indicator, indicator);
411 assert(SQL_SUCCEEDED(sqlResult));
412 assert(std::cmp_equal(*indicator, totalCharsRequired *
sizeof(
char16_t)));
417 stmt, column, CType, (SQLPOINTER) StringTraits::Data(result), (SQLLEN) StringTraits::Size(result), indicator);
420 static SQLRETURN GetColumn(SQLHSTMT stmt,
422 Utf16StringType* result,
424 SqlDataBinderCallback
const& cb)
noexcept
426 return detail::GetColumnUtf16(stmt, column, result, indicator, cb);
429 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(Utf16StringType
const& value)
noexcept
431 auto u8String =
ToUtf8(detail::SqlViewHelper<Utf16StringType>::View(value));
432 return std::string(
reinterpret_cast<char const*
>(u8String.data()), u8String.size());
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>
443 using ValueType = Utf32StringType;
444 using CharType =
typename Utf32StringType::value_type;
445 using StringTraits = SqlBasicStringOperations<Utf32StringType>;
447 static constexpr auto ColumnType = StringTraits::ColumnType;
449 static SQLRETURN InputParameter(SQLHSTMT stmt,
451 Utf32StringType
const& value,
452 SqlDataBinderCallback& cb)
noexcept
454 switch (cb.ServerType())
456 case SqlServerType::POSTGRESQL: {
458 auto u8String = std::make_shared<std::u8string>(
ToUtf8(detail::SqlViewHelper<Utf32StringType>::View(value)));
459 cb.PlanPostExecuteCallback([u8String = u8String]() {});
460 return SQLBindParameter(stmt,
467 (SQLPOINTER) u8String->data(),
471 case SqlServerType::ORACLE:
472 case SqlServerType::MYSQL:
473 case SqlServerType::SQLITE:
474 case SqlServerType::MICROSOFT_SQL:
475 case SqlServerType::UNKNOWN: {
477 std::make_shared<std::u16string>(
ToUtf16(detail::SqlViewHelper<Utf32StringType>::View(value)));
478 cb.PlanPostExecuteCallback([u8String = u16String]() {});
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 };
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);
492 static SQLRETURN OutputColumn(
493 SQLHSTMT stmt, SQLUSMALLINT column, Utf32StringType* result, SQLLEN* indicator, SqlDataBinderCallback& cb)
noexcept
495 return detail::BindOutputColumnNonUtf16Unicode<Utf32StringType>(stmt, column, result, indicator, cb);
498 static SQLRETURN GetColumn(SQLHSTMT stmt,
500 Utf32StringType* result,
502 SqlDataBinderCallback
const& cb)
noexcept
504 auto u16String = std::u16string {};
505 auto const sqlResult = detail::GetColumnUtf16(stmt, column, &u16String, indicator, cb);
506 if (!SQL_SUCCEEDED(sqlResult))
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));
516 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(Utf32StringType
const& value)
noexcept
518 auto u8String =
ToUtf8(detail::SqlViewHelper<Utf32StringType>::View(value));
519 return std::string(
reinterpret_cast<char const*
>(u8String.data()), u8String.size());
524template <
typename Utf8StringType>
525 requires SqlBasicStringBinderConcept<Utf8StringType, char8_t>
526struct SqlDataBinder<Utf8StringType>
528 using ValueType = Utf8StringType;
529 using CharType = char8_t;
530 using StringTraits = SqlBasicStringOperations<Utf8StringType>;
532 static constexpr auto ColumnType = StringTraits::ColumnType;
534 static SQLRETURN InputParameter(SQLHSTMT stmt,
536 Utf8StringType
const& value,
537 SqlDataBinderCallback& cb)
noexcept
539 switch (cb.ServerType())
541 case SqlServerType::POSTGRESQL: {
543 return SQLBindParameter(stmt,
550 (SQLPOINTER) value.data(),
554 case SqlServerType::ORACLE:
555 case SqlServerType::MYSQL:
556 case SqlServerType::SQLITE:
557 case SqlServerType::MICROSOFT_SQL:
558 case SqlServerType::UNKNOWN: {
560 std::make_shared<std::u16string>(
ToUtf16(detail::SqlViewHelper<Utf8StringType>::View(value)));
561 cb.PlanPostExecuteCallback([u16String = u16String]() {});
563 auto const CType = SQL_C_WCHAR;
564 auto const charCount = u16String->size();
565 auto const byteCount = u16String->size() *
sizeof(char16_t);
567 static_cast<SQLSMALLINT
>(charCount > SqlOptimalMaxColumnSize ? SQL_WLONGVARCHAR : SQL_WVARCHAR);
568 return SQLBindParameter(stmt,
575 (SQLPOINTER) u16String->data(),
583 static SQLRETURN OutputColumn(
584 SQLHSTMT stmt, SQLUSMALLINT column, Utf8StringType* result, SQLLEN* indicator, SqlDataBinderCallback& cb)
noexcept
586 return detail::BindOutputColumnNonUtf16Unicode<Utf8StringType>(stmt, column, result, indicator, cb);
589 static SQLRETURN GetColumn(SQLHSTMT stmt,
591 Utf8StringType* result,
593 SqlDataBinderCallback
const& cb)
noexcept
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);
603 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(Utf8StringType
const& value)
noexcept
606 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)