From 057c8299fb1fec24338404b28f2b33473e547bc8 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 24 Jan 2013 15:10:21 +0200 Subject: Add support for mapping char[N] to CHAR/VARCHAR database types Also improve query support for arrays (decaying). --- odb/mssql/traits.txx | 262 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 238 insertions(+), 24 deletions(-) (limited to 'odb/mssql/traits.txx') diff --git a/odb/mssql/traits.txx b/odb/mssql/traits.txx index 634097e..1e2db27 100644 --- a/odb/mssql/traits.txx +++ b/odb/mssql/traits.txx @@ -11,7 +11,6 @@ namespace odb // // wrapped_value_traits // - template void wrapped_value_traits:: result_callback (void* context, @@ -52,11 +51,10 @@ namespace odb } // - // c_array_long_binary_value_traits + // c_array_long_value_traits_base // - - template - void c_array_long_binary_value_traits:: + template + void c_array_long_value_traits_base:: param_callback (const void* context, std::size_t*, const void** buffer, @@ -65,64 +63,281 @@ namespace odb void*, std::size_t) { + // Figure out the length. We cannot use strlen since it may + // not be 0-terminated (strnlen is not standard). + // + size_t n (0); + for (; n != N && static_cast (context)[n] != '\0'; ++n); + *buffer = context; - *size = N; + *size = n; *chunk = chunk_one; } - template - void c_array_long_binary_value_traits:: + template + void c_array_long_value_traits_base:: result_callback (void* context, - std::size_t*, + std::size_t* position, void** buffer, std::size_t* size, chunk_type chunk, - std::size_t size_left, + std::size_t, void* tmp_buf, std::size_t tmp_capacity) { - // The code is similar to the vector specialization. - // + char* p (static_cast (context)); + switch (chunk) { case chunk_null: case chunk_one: { - std::memset (context, 0, N); + *p = '\0'; break; } case chunk_first: { - assert (size_left != 0); + *buffer = p; + *size = N; + break; + } + case chunk_next: + { + // ODBC insists on appending '\0' to each chunk it returns. + // As a result, we can get here if the last character did not + // fit into our buffer. There could also be more data but since + // there is no way to stop until we read all the data, dump + // the remainder into the temporary buffer. + // + + // Use position to indicate whether this is the first "next + // chunk". + // + if (*position == 0) + *position = 1; + else if (*position == 1) + { + p[N - 1] = *static_cast (tmp_buf); + *position = 2; + } + + *buffer = tmp_buf; + *size = tmp_capacity; + break; + } + case chunk_last: + { + if (*position == 0) + { + if (*size < N) // Append '\0' if there is space. + p[*size] = '\0'; + } + else if (*position == 1) + p[N - 1] = *static_cast (tmp_buf); - *buffer = context; - *size = size_left < N ? size_left : N; + break; + } + } + } + + // + // c_warray_long_value_traits_base<2> + // + template + void c_warray_long_value_traits_base:: + param_callback (const void* context, + std::size_t*, + const void** buffer, + std::size_t* size, + chunk_type* chunk, + void*, + std::size_t) + { + // Figure out the length. We cannot use wcslen since it may + // not be 0-terminated (wcsnlen is not standard). + // + size_t n (0); + for (; n != N && static_cast (context)[n] != L'\0'; ++n); + + *buffer = context; + *size = n * 2; // In bytes. + *chunk = chunk_one; + } + + template + void c_warray_long_value_traits_base:: + result_callback (void* context, + std::size_t* position, + void** buffer, + std::size_t* size, + chunk_type chunk, + std::size_t, + void* tmp_buf, + std::size_t tmp_capacity) + { + wchar_t* p (static_cast (context)); + + switch (chunk) + { + case chunk_null: + case chunk_one: + { + *p = L'\0'; + break; + } + case chunk_first: + { + *buffer = p; + *size = N * 2; // In bytes break; } case chunk_next: { - // We can get here if total size is greater than N. There is - // no way to stop until we read all the data, so dump the - // remainder into the temporary buffer. + // ODBC insists on appending '\0' to each chunk it returns. + // As a result, we can get here if the last character did not + // fit into our buffer. There could also be more data but since + // there is no way to stop until we read all the data, dump + // the remainder into the temporary buffer. // + + // Use position to indicate whether this is the first "next + // chunk". + // + if (*position == 0) + *position = 1; + else if (*position == 1) + { + p[N - 1] = *static_cast (tmp_buf); + *position = 2; + } + *buffer = tmp_buf; *size = tmp_capacity; break; } case chunk_last: { + if (*position == 0) + { + size_t n (*size / 2); // In wide characters. + if (n < N) // Append '\0' if there is space. + p[n] = L'\0'; + } + else if (*position == 1) + p[N - 1] = *static_cast (tmp_buf); + break; } } } -#ifdef ODB_CXX11 +#ifndef _WIN32 // - // array_long_binary_value_traits + // c_warray_long_value_traits_base<4> // + template + void c_warray_long_value_traits_base:: + param_callback (const void* context, + std::size_t* pos, + const void** buffer, + std::size_t* size, + chunk_type* chunk, + void* tmp_buf, + std::size_t tmp_capacity) + { + // Here we cannot just return the pointer to the underlying buffer + // since the character sizes are different. Instead we copy the + // data to the temporary buffer. + // + ucs2_char* d (static_cast (tmp_buf)); + const wchar_t* s (static_cast (context) + *pos); + + size_t n (0); + tmp_capacity /= 2; // In UCS-2 character. + for (const wchar_t* e (s + N); + s != e && *s != L'\0' && n != tmp_capacity; + ++n, ++s) + d[n] = static_cast (*s); + + *buffer = d; + *size = n * 2; // In bytes. + *chunk = (n != tmp_capacity ? chunk_last : chunk_next); + if (*pos == 0) + { + if (*chunk == chunk_last) + *chunk = chunk_one; + else + *chunk = chunk_first; + } + + *pos += n; + } + + template + void c_warray_long_value_traits_base:: + result_callback (void* context, + std::size_t* pos, + void** buffer, + std::size_t* size, + chunk_type chunk, + std::size_t, + void* tmp_buf, + std::size_t tmp_capacity) + { + wchar_t* d (static_cast (context)); + const ucs2_char* s (static_cast (tmp_buf)); + + // Again, we cannot do direct buffer copy and have to use the + // temporary buffer instead. + // + switch (chunk) + { + case chunk_null: + case chunk_one: + { + *d = L'\0'; + break; + } + case chunk_first: + { + break; + } + case chunk_next: + case chunk_last: + { + // Append the data from the temporary buffer. + // + if (*pos < N) + { + *size /= 2; // In UCS-2 characters. + size_t n (N - *pos); + n = *size < n ? *size : n; + + wstring_functions<>::assign (d + *pos, s, n); + *pos += n; + + if (*pos < N) // Append '\0' if there is space. + d[*pos] = L'\0'; + } + + break; + } + } + + if (chunk == chunk_first || chunk == chunk_next) + { + *buffer = tmp_buf; + *size = tmp_capacity; + } + } +#endif + + // + // c_array_long_binary_value_traits + // template - void array_long_binary_value_traits:: + void c_array_long_binary_value_traits:: param_callback (const void* context, std::size_t*, const void** buffer, @@ -137,7 +352,7 @@ namespace odb } template - void array_long_binary_value_traits:: + void c_array_long_binary_value_traits:: result_callback (void* context, std::size_t*, void** buffer, @@ -181,6 +396,5 @@ namespace odb } } } -#endif } } -- cgit v1.1