diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2011-12-21 11:19:24 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2011-12-21 11:19:24 +0200 |
commit | 28b329b63a3919020a9286b557f7938dc8fbd746 (patch) | |
tree | 45ff48d166a6dd4f02f710fe96d34fd10b4003e9 | |
parent | 213740c161499a5961fa426d1b77c7d68763c5df (diff) |
ODB compiler implementation, traits, and types test for SQL Server
-rw-r--r-- | odb/mssql/mssql-fwd.hxx | 72 | ||||
-rw-r--r-- | odb/mssql/mssql-types.hxx | 103 | ||||
-rw-r--r-- | odb/mssql/mssql.hxx | 5 | ||||
-rw-r--r-- | odb/mssql/statement.cxx | 307 | ||||
-rw-r--r-- | odb/mssql/traits.cxx | 515 | ||||
-rw-r--r-- | odb/mssql/traits.hxx | 1118 | ||||
-rw-r--r-- | odb/mssql/traits.txx | 109 |
7 files changed, 2045 insertions, 184 deletions
diff --git a/odb/mssql/mssql-fwd.hxx b/odb/mssql/mssql-fwd.hxx index 20acab3..fbb57f7 100644 --- a/odb/mssql/mssql-fwd.hxx +++ b/odb/mssql/mssql-fwd.hxx @@ -93,6 +93,12 @@ typedef SQLHANDLE SQLHDESC; # define SQL_HANDLE_DESC 4 #endif +#ifndef SQL_NULL_DATA +# define SQL_NULL_DATA (-1) +# define SQL_DATA_AT_EXEC (-2) +# define SQL_NO_TOTAL (-4) +#endif + // The following types are our own equivalents of ODBC and Native Client // ODBC driver types. They are all PODs and should be layout-compatible // with the original types, which means they can be used interchangeably. @@ -122,7 +128,7 @@ namespace odb struct decimal { unsigned char precision; - signed char scale; + signed char scale; unsigned char sign; // 1 - positive, 0 - negative unsigned char val[SQL_MAX_NUMERIC_LEN]; }; @@ -144,12 +150,66 @@ namespace odb int value; // 4-byte signed integer containing value * 10,000. }; - //@@ TODO + // SQL_DATE_STRUCT + // + struct date + { + SQLSMALLINT year; + SQLUSMALLINT month; + SQLUSMALLINT day; + }; + + // SQL_SS_TIME2_STRUCT + // +#pragma pack(push,8) + struct time + { + SQLUSMALLINT hour; + SQLUSMALLINT minute; + SQLUSMALLINT second; + SQLUINTEGER fraction; + }; +#pragma pack(pop) + + // SQL_TIMESTAMP_STRUCT // - struct date {}; - struct time {}; - struct datetime {}; - struct datetimeoffset {}; + struct datetime + { + SQLSMALLINT year; + SQLUSMALLINT month; + SQLUSMALLINT day; + SQLUSMALLINT hour; + SQLUSMALLINT minute; + SQLUSMALLINT second; + SQLUINTEGER fraction; + }; + + // SQL_SS_TIMESTAMPOFFSET_STRUCT + // +#pragma pack(push,8) + struct datetimeoffset + { + SQLSMALLINT year; + SQLUSMALLINT month; + SQLUSMALLINT day; + SQLUSMALLINT hour; + SQLUSMALLINT minute; + SQLUSMALLINT second; + SQLUINTEGER fraction; + SQLSMALLINT timezone_hour; + SQLSMALLINT timezone_minute; + }; +#pragma pack(pop) + + // SQLGUID + // + struct uniqueidentifier + { + unsigned int data1; + unsigned short data2; + unsigned short data3; + unsigned char data4[8]; + }; } } diff --git a/odb/mssql/mssql-types.hxx b/odb/mssql/mssql-types.hxx index caedda3..b5c43b4 100644 --- a/odb/mssql/mssql-types.hxx +++ b/odb/mssql/mssql-types.hxx @@ -19,19 +19,21 @@ namespace odb { enum chunk_type { - first_chunk, - next_chunk, - last_chunk, - one_chunk, - null_chunk + chunk_null, + chunk_one, + chunk_first, + chunk_next, + chunk_last, }; typedef void (*param_callback_type) ( const void* context, // User context. - std::size_t* position, // Position context. Am implementation is free + std::size_t* position, // Position context. An implementation is free // to use this to track position information. It // is initialized to zero before the first call. - const void** buffer, // [out] Buffer contaning the data. + const void** buffer, // [in/out] Buffer contaning the data. On the + // the first call it contains a pointer to the + // long_callback struct (used for redirections). std::size_t* size, // [out] Data size. chunk_type*, // [out] The position of this chunk of data. void* temp_buffer, // A temporary buffer that may be used by the @@ -40,18 +42,20 @@ namespace odb typedef void (*result_callback_type) ( void* context, // User context. - std::size_t* position, // Position context. Am implementation is free + std::size_t* position, // Position context. An implementation is free // to use this to track position information. It // is initialized to zero before the first call. - void** buffer, // [out] Buffer to copy the data to. + void** buffer, // [in/out] Buffer to copy the data to. On the + // the first call it contains a pointer to the + // long_callback struct (used for redirections). std::size_t* size, // [in/out] In: amount of data copied into the // buffer after the previous call. Out: capacity // of the buffer. - chunk_type, // The position of this chunk; first_chunk means - // this is the first call, last_chunk means there - // is no more data, null_chunk means this value is - // NULL, and one_chunk means the value is empty - // (in this case *size is 0). + chunk_type, // The position of this chunk; chunk_first means + // this is the first call, chunk_last means there + // is no more data, chunk_null means this value is + // NULL, and chunk_one means the value is empty + // (in this case *total_size is 0). std::size_t size_left, // Contains the amount of data left or 0 if this // information is not available. void* temp_buffer, // A temporary buffer that may be used by the @@ -81,50 +85,59 @@ namespace odb // enum buffer_type { - bit, // Buffer is a 1-byte integer. - tinyint, // Buffer is a 1-byte integer. - smallint, // Buffer is a 2-byte integer. - int_, // Buffer is a 4-byte integer. - bigint, // Buffer is an 8-byte integer. + bit, // Buffer is a 1-byte integer. + tinyint, // Buffer is a 1-byte integer. + smallint, // Buffer is a 2-byte integer. + int_, // Buffer is a 4-byte integer. + bigint, // Buffer is an 8-byte integer. - decimal, // Buffer is a decimal struct (SQL_NUMERIC_STRUCT). + decimal, // Buffer is a decimal struct (SQL_NUMERIC_STRUCT). - smallmoney, // Buffer is a smallmoney struct (DBMONEY4). - money, // Buffer is a money struct (DBMONEY). + smallmoney, // Buffer is a smallmoney struct (DBMONEY4). + money, // Buffer is a money struct (DBMONEY). - float4, // Buffer is a float. - float8, // Buffer is a double. + float4, // Buffer is a float. + float8, // Buffer is a double. - string, // Buffer is a char array. - long_string, // Buffer is a long_callback. + string, // Buffer is a char array. + long_string, // Buffer is a long_callback. - nstring, // Buffer is a wchar_t (2-byte) array. - long_nstring, // Buffer is a long_callback. + nstring, // Buffer is a wchar_t (2-byte) array. + long_nstring, // Buffer is a long_callback. - binary, // Buffer is a byte array. - long_binary, // Buffer is a long_callback. + binary, // Buffer is a byte array. + long_binary, // Buffer is a long_callback. - /* - date, // Buffer is an SQL_DATE_STRUCT. - time, // Buffer is an SQL_SS_TIME2_STRUCT. - datetime, // Buffer is an SQL_TIMESTAMP_STRUCT. - datetimeoffset, // Buffer is an SQL_SS_TIMESTAMPOFFSET_STRUCT. + date, // Buffer is an SQL_DATE_STRUCT. + time, // Buffer is an SQL_SS_TIME2_STRUCT. + datetime, // Buffer is an SQL_TIMESTAMP_STRUCT. + datetimeoffset, // Buffer is an SQL_SS_TIMESTAMPOFFSET_STRUCT. - uuid, // Buffer is a 16-byte array (or SQLGUID). - rowversion, // Buffer is an 8-byte array. - */ + uniqueidentifier, // Buffer is an SQLGUID. + rowversion, // Buffer is an 8-byte array. - last // Used as an end of list marker. + last // Used as an end of list marker. }; buffer_type type; // The buffer type. void* buffer; // The buffer. For long data this is a long_callback. - SQLLEN* size_ind; // Pointer to the size/inidicator variable. - SQLLEN capacity; // Buffer capacity. For string/binary parameters - // this value is also used as maximum column size. - // For decimal parameters it contains precision (p) - // and scale (s) encoded as (p * 100 + s). For float4 - // and float8 it contains precision. + SQLLEN* size_ind; // Pointer to the size/inidicator variable. Size is + // ignored except for variable-size, [small]money, and + // rowversion types. Sepcial indicator values are + // SQL_NULL_DATA (value is NULL) and SQL_DATA_AT_EXEC + // (should be set for the long_* data types). + SQLLEN capacity; // Buffer capacity. Only used for variable-size + // types as well as to pass column/size precisions + // as follows: For string/binary parameters this + // value (minus one character for strings) is used + // as maximum column size. For decimal parameters + // it contains precision (p) and scale (s) encoded + // as (p * 100 + s). For float4 and float8 it + // contains precision. For time, datetime, and + // datatimeoffset it contains fractional seconds + // (scale). In case of datetime, the special + // value 8 indicates the SMALLDATETIME type + // which the the seconds precision. }; } } diff --git a/odb/mssql/mssql.hxx b/odb/mssql/mssql.hxx index 8c455ba..f223837 100644 --- a/odb/mssql/mssql.hxx +++ b/odb/mssql/mssql.hxx @@ -47,6 +47,11 @@ # define SQL_MARS_ENABLED_YES 1L #endif +#ifndef SQL_SS_TIME2 +# define SQL_SS_TIME2 (-154) +# define SQL_SS_TIMESTAMPOFFSET (-155) +#endif + // unixODBC doesn't define SQL_PARAM_DATA_AVAILABLE even though it // claims ODBC version 3.80. // diff --git a/odb/mssql/statement.cxx b/odb/mssql/statement.cxx index 0f05e95..73c7649 100644 --- a/odb/mssql/statement.cxx +++ b/odb/mssql/statement.cxx @@ -25,54 +25,70 @@ namespace odb // static const SQLSMALLINT sql_type_lookup [bind::last] = { - SQL_BIT, // bind::bit - SQL_TINYINT, // bind::tinyint - SQL_SMALLINT, // bind::smallint - SQL_INTEGER, // bind::integer - SQL_BIGINT, // bind::bigint + SQL_BIT, // bind::bit + SQL_TINYINT, // bind::tinyint + SQL_SMALLINT, // bind::smallint + SQL_INTEGER, // bind::integer + SQL_BIGINT, // bind::bigint - SQL_DECIMAL, // bind::decimal - SQL_DECIMAL, // bind::smallmoney - SQL_DECIMAL, // bind::money + SQL_DECIMAL, // bind::decimal + SQL_DECIMAL, // bind::smallmoney + SQL_DECIMAL, // bind::money - SQL_FLOAT, // bind::float4 - SQL_FLOAT, // bind::float8 + SQL_FLOAT, // bind::float4 + SQL_FLOAT, // bind::float8 - SQL_VARCHAR, // bind::string - SQL_VARCHAR, // bind::long_string + SQL_VARCHAR, // bind::string + SQL_VARCHAR, // bind::long_string - SQL_WVARCHAR, // bind::nstring - SQL_WVARCHAR, // bind::long_nstring + SQL_WVARCHAR, // bind::nstring + SQL_WVARCHAR, // bind::long_nstring - SQL_VARBINARY, // bind::binary - SQL_VARBINARY // bind::long_binary + SQL_VARBINARY, // bind::binary + SQL_VARBINARY, // bind::long_binary + + SQL_TYPE_DATE, // bind::date + SQL_SS_TIME2, // bind::time + SQL_TYPE_TIMESTAMP, // bind::datetime + SQL_SS_TIMESTAMPOFFSET, // bind::datetimeoffset + + SQL_GUID, // bind::uniqueidentifier + SQL_BINARY // bind::rowversion }; // Mapping of bind::buffer_type to SQL_C_* C types. // static const SQLSMALLINT c_type_lookup [bind::last] = { - SQL_C_BIT, // bind::bit - SQL_C_UTINYINT, // bind::tinyint - SQL_C_SSHORT, // bind::smallint - SQL_C_SLONG, // bind::integer - SQL_C_SBIGINT, // bind::bigint + SQL_C_BIT, // bind::bit + SQL_C_UTINYINT, // bind::tinyint + SQL_C_SSHORT, // bind::smallint + SQL_C_SLONG, // bind::integer + SQL_C_SBIGINT, // bind::bigint + + SQL_C_NUMERIC, // bind::decimal + SQL_C_BINARY, // bind::smallmoney + SQL_C_BINARY, // bind::money + + SQL_C_FLOAT, // bind::float4 + SQL_C_DOUBLE, // bind::float8 - SQL_C_NUMERIC, // bind::decimal - SQL_C_BINARY, // bind::smallmoney - SQL_C_BINARY, // bind::money + SQL_C_CHAR, // bind::string + SQL_C_CHAR, // bind::long_string - SQL_C_FLOAT, // bind::float4 - SQL_C_DOUBLE, // bind::float8 + SQL_C_WCHAR, // bind::nstring + SQL_C_WCHAR, // bind::long_nstring - SQL_C_CHAR, // bind::string - SQL_C_CHAR, // bind::long_string + SQL_C_BINARY, // bind::binary + SQL_C_BINARY, // bind::long_binary - SQL_C_WCHAR, // bind::nstring - SQL_C_WCHAR, // bind::long_nstring + SQL_C_TYPE_DATE, // bind::date + SQL_C_BINARY, // bind::time + SQL_C_TYPE_TIMESTAMP, // bind::datetime + SQL_C_BINARY, // bind::datetimeoffset - SQL_C_BINARY, // bind::binary - SQL_C_BINARY // bind::long_binary + SQL_C_GUID, // bind::uniqueidentifier + SQL_C_BINARY // bind::rowversion }; // @@ -177,7 +193,7 @@ namespace odb for (size_t i (1); i < n; ++i, ++b) { - SQLULEN col_size; + SQLULEN col_size (0); SQLSMALLINT digits (0); SQLPOINTER buf; @@ -229,6 +245,11 @@ namespace odb break; } case bind::string: + { + buf = (SQLPOINTER) b->buffer; + col_size = (SQLULEN) b->capacity - 1; // Sans the null-terminator. + break; + } case bind::binary: { buf = (SQLPOINTER) b->buffer; @@ -238,13 +259,78 @@ namespace odb case bind::nstring: { buf = (SQLPOINTER) b->buffer; - col_size = (SQLULEN) b->capacity / 2; // In characters, not bytes. + // In characters, not bytes, and sans the null-terminator. + col_size = (SQLULEN) (b->capacity / 2 - 1); + break; + } + case bind::date: + { + buf = (SQLPOINTER) b->buffer; + // Native Client 10.0 requires the correct precision. + // + col_size = 10; + break; + } + case bind::time: + { + buf = (SQLPOINTER) b->buffer; + digits = (SQLULEN) b->capacity; + + // Native Client 10.0 requires the correct precision. + // + if (digits == 0) + col_size = 8; + else + col_size = digits + 9; + + break; + } + case bind::datetime: + { + buf = (SQLPOINTER) b->buffer; + digits = (SQLULEN) b->capacity; + + // Native Client 10.0 requires the correct precision. + // + if (digits == 0) + col_size = 19; + else if (digits == 8) + { + // This is a SMALLDATETIME column which only has the minutes + // precision. Documentation indicates that the correct numeric + // precision value for this type is 16. + // + digits = 0; + col_size = 16; + } + else + col_size = digits + 20; + + break; + } + case bind::datetimeoffset: + { + buf = (SQLPOINTER) b->buffer; + digits = (SQLULEN) b->capacity; + + // Native Client 10.0 requires the correct precision. + // + if (digits == 0) + col_size = 26; + else + col_size = digits + 27; + + break; + } + case bind::rowversion: + { + buf = (SQLPOINTER) b->buffer; + col_size = 8; break; } default: { buf = (SQLPOINTER) b->buffer; - col_size = 0; break; } } @@ -278,6 +364,27 @@ namespace odb switch (b->type) { + case bind::bit: + case bind::tinyint: + { + cap = 1; + break; + } + case bind::smallint: + { + cap = 2; + break; + } + case bind::int_: + { + cap = 4; + break; + } + case bind::bigint: + { + cap = 8; + break; + } case bind::decimal: { cap = (SQLLEN) sizeof (decimal); @@ -285,22 +392,29 @@ namespace odb } case bind::smallmoney: { - cap = (SQLLEN) sizeof (smallmoney); + cap = 4; break; } case bind::money: { - cap = (SQLLEN) sizeof (money); + cap = 8; break; } case bind::float4: { - cap = (SQLLEN) sizeof (float); + cap = 4; break; } case bind::float8: { - cap = (SQLLEN) sizeof (double); + cap = 8; + break; + } + case bind::string: + case bind::nstring: + case bind::binary: + { + cap = b->capacity; break; } case bind::long_string: @@ -311,9 +425,39 @@ namespace odb // continue; } - default: + case bind::date: { - cap = b->capacity; + cap = (SQLLEN) sizeof (date); + break; + } + case bind::time: + { + cap = (SQLLEN) sizeof (time); + break; + } + case bind::datetime: + { + cap = (SQLLEN) sizeof (datetime); + break; + } + case bind::datetimeoffset: + { + cap = (SQLLEN) sizeof (datetimeoffset); + break; + } + case bind::uniqueidentifier: + { + cap = 16; + break; + } + case bind::rowversion: + { + cap = 8; + break; + } + case bind::last: + { + assert (false); break; } } @@ -365,48 +509,41 @@ namespace odb if (r != SQL_NEED_DATA) break; - // See if we have a NULL value. + long_callback& cb (*static_cast<long_callback*> (b->buffer)); + + // Store the pointer to the long_callback struct in buf on the + // first call to the callback. This allows the callback to + // redirect further calls to some other callback. // - if (*b->size_ind == SQL_NULL_DATA) + const void* buf (&cb); + + size_t position (0); + for (;;) { - r = SQLPutData (stmt_, 0, SQL_NULL_DATA); + size_t n; + chunk_type chunk; + + cb.callback.param ( + cb.context.param, + &position, + &buf, + &n, + &chunk, + tmp_buf.data (), + tmp_buf.capacity ()); + + r = SQLPutData ( + stmt_, + (SQLPOINTER) (buf != 0 ? buf : &buf), // Always pass non-NULL. + chunk != chunk_null ? (SQLLEN) n : SQL_NULL_DATA); if (!SQL_SUCCEEDED (r)) translate_error (r, conn_, stmt_); - } - else - { - long_callback& cb (*static_cast<long_callback*> (b->buffer)); - size_t position (0); - for (;;) - { - size_t n; - const void* buf; - chunk_type chunk; - - cb.callback.param ( - cb.context.param, - &position, - &buf, - &n, - &chunk, - tmp_buf.data (), - tmp_buf.capacity ()); - - r = SQLPutData ( - stmt_, - (SQLPOINTER) buf, - chunk != null_chunk ? (SQLLEN) n : SQL_NULL_DATA); - - if (!SQL_SUCCEEDED (r)) - translate_error (r, conn_, stmt_); - - if (chunk == one_chunk || - chunk == last_chunk || - chunk == null_chunk) - break; - } + if (chunk == chunk_one || + chunk == chunk_last || + chunk == chunk_null) + break; } } } @@ -463,14 +600,18 @@ namespace odb if (!SQL_SUCCEEDED (r)) translate_error (r, conn_, stmt_); - void* buf; + // Store the pointer to the long_callback struct in buf on the + // first call to the callback. This allows the callback to + // redirect further calls to some other callback. + // + void* buf (&cb); size_t size (0); size_t position (0); size_t size_left (si == SQL_NO_TOTAL ? 0 : static_cast<size_t> (si)); chunk_type c (si == SQL_NULL_DATA - ? null_chunk - : (si == 0 ? one_chunk : first_chunk)); + ? chunk_null + : (si == 0 ? chunk_one : chunk_first)); for (;;) { @@ -484,7 +625,7 @@ namespace odb tmp_buf.data (), tmp_buf.capacity ()); - if (c == last_chunk || c == one_chunk || c == null_chunk) + if (c == chunk_last || c == chunk_one || c == chunk_null) break; // SQLGetData() can keep returning SQL_SUCCESS_WITH_INFO (truncated) @@ -507,14 +648,14 @@ namespace odb // include the NULL teminator). // size = static_cast<size_t> (si); - c = last_chunk; + c = chunk_last; } else if (r == SQL_SUCCESS_WITH_INFO) { if (char_data) size--; // NULL terminator. - c = next_chunk; + c = chunk_next; } else translate_error (r, conn_, stmt_); diff --git a/odb/mssql/traits.cxx b/odb/mssql/traits.cxx index 6c625c6..3560afa 100644 --- a/odb/mssql/traits.cxx +++ b/odb/mssql/traits.cxx @@ -3,6 +3,8 @@ // copyright : Copyright (c) 2005-2011 Code Synthesis Tools CC // license : ODB NCUEL; see accompanying LICENSE file +#include <cassert> + #include <odb/mssql/traits.hxx> using namespace std; @@ -11,5 +13,518 @@ namespace odb { namespace mssql { + // + // default_value_traits<std::string, id_long_string> + // + + void default_value_traits<string, id_long_string>:: + param_callback (const void* context, + size_t*, + const void** buffer, + size_t* size, + chunk_type* chunk, + void*, + size_t) + { + const string& str (*static_cast<const string*> (context)); + + *buffer = str.c_str (); + *size = str.size (); + *chunk = chunk_one; + } + + void default_value_traits<string, id_long_string>:: + result_callback (void* context, + size_t* position, + void** buffer, + size_t* size, + chunk_type chunk, + size_t size_left, + void* tmp_buf, + size_t tmp_capacity) + { + string& str (*static_cast<string*> (context)); + + switch (chunk) + { + case chunk_null: + case chunk_one: + { + str.clear (); + break; + } + case chunk_first: + { + // If the total size is available, then pre-allocate the string + // and copy the data directly into its buffer in one go. While + // this kind of direct modification of the std::string buffer + // is not sanctioned by the standard, this is known to work + // with all the implementations we care to support. We just + // need to make sure the underlying buffer is not shared with + // any other instance if the implementation uses COW. + // + if (size_left != 0) + { + size_left++; // One extra for the null terminator. + + if (str.size () != size_left) + str.resize (size_left); + else + str[0] = '\0'; // Force copy in a COW implementation. + + *buffer = const_cast<char*> (str.c_str ()); + *size = size_left; + *position = 0; // Indicator. + } + else + { + // If the total size is not available, do the short string + // optimization by first returning a small temporary buffer. + // If the data fits, then we copy it over to the string and + // thus get the precise buffer allocation. If the data does + // not fit, then we resort to the exponential buffer growth. + // + *buffer = tmp_buf; + *size = tmp_capacity > 128 ? 128 : tmp_capacity; + *position = 1; // Indicator. + } + + break; + } + case chunk_next: + { + // We should only end up here if the short string optimization + // didn't work out. + // + assert (*position != 0); + + if (*position == 1) + { + // First chunk_next call. There is some data in the temp + // buffer which we need to copy over. + // + str.reserve (256); + str.assign (static_cast<char*> (tmp_buf), *size); + *position = *size; // Size of actual data in str, which got to be + // greater than 128, or we would have gotten + // chunk_last. + str.resize (256); + } + else + { + // Subsequent chunk_next call. Double the buffer and continue. + // + *position += *size; + str.resize (str.size () * 2); + } + + *buffer = const_cast<char*> (str.c_str ()) + *position; + *size = str.size () - *position; + break; + } + case chunk_last: + { + if (*position == 0) + str.resize (*size); + else if (*position == 1) + // Short string optimization worked out. There is some data + // in the temp buffer which we need to copy over. + // + str.assign (static_cast<char*> (tmp_buf), *size); + else + str.resize (*position + *size); + + break; + } + } + } + + // + // c_long_string_value_traits + // + + void c_string_long_value_traits:: + param_callback (const void* context, + size_t*, + const void** buffer, + size_t* size, + chunk_type* chunk, + void*, + size_t) + { + *buffer = context; + *size = strlen (static_cast<const char*> (context)); + *chunk = chunk_one; + } + + // + // wstring_long_value_traits<2> + // + + void wstring_long_value_traits<2>:: + param_callback (const void* context, + size_t*, + const void** buffer, + size_t* size, + chunk_type* chunk, + void*, + size_t) + { + const wstring& str (*static_cast<const wstring*> (context)); + + *buffer = str.c_str (); + *size = str.size () * 2; // In bytes. + *chunk = chunk_one; + } + + void wstring_long_value_traits<2>:: + result_callback (void* context, + size_t*, + void** buffer, + size_t* size, + chunk_type chunk, + size_t size_left, + void*, + size_t) + { + wstring& str (*static_cast<wstring*> (context)); + + switch (chunk) + { + case chunk_null: + case chunk_one: + { + str.clear (); + break; + } + case chunk_first: + { + // The Native Client ODBC driver seems to always be able to + // return the total size for national character strings. This + // makes things simple and efficient. + // + assert (size_left != 0); + + size_left /= 2; // Convert to characters. + size_left++; // One extra for the null terminator. + + if (str.size () != size_left) + str.resize (size_left); + else + str[0] = L'\0'; // Force copy in a COW implementation. + + *buffer = const_cast<wchar_t*> (str.c_str ()); + *size = size_left * 2; // In bytes. + break; + } + case chunk_next: + { + // We should never get here. + // + assert (false); + break; + } + case chunk_last: + { + str.resize (*size / 2); // Get rid of the null terminator. + break; + } + } + } + + // + // wstring_long_value_traits<4> + // + +#ifndef _WIN32 + void wstring_long_value_traits<4>:: + param_callback (const void* context, + size_t* position, + const void** buffer, + size_t* size, + chunk_type* chunk, + void* tmp_buf, + size_t tmp_capacity) + { + const wstring& str (*static_cast<const wstring*> (context)); + + // 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. + // + *buffer = tmp_buf; + *size = str.size () - *position; // In UCS-2 characters. + + if (*size > tmp_capacity / 2) + { + *size = tmp_capacity / 2; + *chunk = chunk_next; + } + else + *chunk = chunk_last; + + wstring_functions<>::assign (static_cast<ucs2_char*> (tmp_buf), + str.c_str () + *position, + *size); + if (*position == 0) + { + if (*chunk == chunk_last) + *chunk = chunk_one; + else + *chunk = chunk_first; + } + + *position += *size; + *size *= 2; // Convert to bytes. + } + + void wstring_long_value_traits<4>:: + result_callback (void* context, + size_t*, + void** buffer, + size_t* size, + chunk_type chunk, + size_t size_left, + void* tmp_buf, + size_t tmp_capacity) + { + wstring& str (*static_cast<wstring*> (context)); + + // Again, we cannot do direct buffer copy and have to use the + // temporary buffer instead. + // + switch (chunk) + { + case chunk_null: + case chunk_one: + { + str.clear (); + break; + } + case chunk_first: + { + // The Native Client ODBC driver seems to always be able to + // return the total size for national character strings. Use + // this to reserve enough space in the string. + // + assert (size_left != 0); + str.reserve (size_left / 2); + break; + } + case chunk_next: + case chunk_last: + { + // Append the data from the temporary buffer. + // + ucs2_char* p (static_cast<ucs2_char*> (tmp_buf)); + str.append (p, p + *size / 2); + break; + } + } + + if (chunk == chunk_first || chunk == chunk_next) + { + *buffer = tmp_buf; + *size = tmp_capacity; + } + } +#endif // _WIN32 + + // + // c_wstring_long_value_traits<2> + // + + void c_wstring_long_value_traits<2>:: + param_callback (const void* context, + size_t*, + const void** buffer, + size_t* size, + chunk_type* chunk, + void*, + size_t) + { + *buffer = context; + *size = wcslen (static_cast<const wchar_t*> (context)) * 2; // In bytes. + *chunk = chunk_one; + } + + // + // c_wstring_long_value_traits<4> + // + +#ifndef _WIN32 + void c_wstring_long_value_traits<4>:: + param_callback (const void* context, + size_t* position, + const void** buffer, + size_t* size, + chunk_type* chunk, + void* tmp_buf, + size_t tmp_capacity) + { + const wchar_t* str (static_cast<const wchar_t*> (context)); + + // Here we cannot just return the pointer to the buffer since the + // character sizes are different. Instead we copy the data to the + // temporary buffer. + // + *buffer = tmp_buf; + *size = wcslen (str) - *position; // In UCS-2 characters. + + if (*size > tmp_capacity / 2) + { + *size = tmp_capacity / 2; + *chunk = chunk_next; + } + else + *chunk = chunk_last; + + wstring_functions<>::assign (static_cast<ucs2_char*> (tmp_buf), + str + *position, + *size); + if (*position == 0) + { + if (*chunk == chunk_last) + *chunk = chunk_one; + else + *chunk = chunk_first; + } + + *position += *size; + *size *= 2; // Convert to bytes. + } +#endif // _WIN32 + + // + // default_value_traits<std::vector<char>, id_long_binary> + // + + void default_value_traits<std::vector<char>, id_long_binary>:: + param_callback (const void* context, + size_t*, + const void** buffer, + size_t* size, + chunk_type* chunk, + void*, + size_t) + { + const value_type& v (*static_cast<const value_type*> (context)); + + *buffer = &v.front (); + *size = v.size (); + *chunk = chunk_one; + } + + void default_value_traits<std::vector<char>, id_long_binary>:: + result_callback (void* context, + size_t*, + void** buffer, + size_t* size, + chunk_type chunk, + size_t size_left, + void*, + size_t) + { + value_type& v (*static_cast<value_type*> (context)); + + switch (chunk) + { + case chunk_null: + case chunk_one: + { + v.clear (); + break; + } + case chunk_first: + { + // The Native Client ODBC driver seems to always be able to + // return the total size. This makes things simple and + // efficient. + // + assert (size_left != 0); + + v.resize (size_left); + *buffer = &v.front (); + *size = size_left; + break; + } + case chunk_next: + { + // We should never get here. + // + assert (false); + break; + } + case chunk_last: + { + // Nothing to do here. The vector is already of the correct size + // and should contain the data. + break; + } + } + } + + // + // default_value_traits<std::vector<unsigned char>, id_long_binary> + // + + void default_value_traits<std::vector<unsigned char>, id_long_binary>:: + param_callback (const void* context, + size_t*, + const void** buffer, + size_t* size, + chunk_type* chunk, + void*, + size_t) + { + const value_type& v (*static_cast<const value_type*> (context)); + + *buffer = &v.front (); + *size = v.size (); + *chunk = chunk_one; + } + + void default_value_traits<std::vector<unsigned char>, id_long_binary>:: + result_callback (void* context, + size_t*, + void** buffer, + size_t* size, + chunk_type chunk, + size_t size_left, + void*, + size_t) + { + // The code is exactly the same as in the vector<char> specialization. + // + value_type& v (*static_cast<value_type*> (context)); + + switch (chunk) + { + case chunk_null: + case chunk_one: + { + v.clear (); + break; + } + case chunk_first: + { + assert (size_left != 0); + + v.resize (size_left); + *buffer = &v.front (); + *size = size_left; + break; + } + case chunk_next: + { + assert (false); + break; + } + case chunk_last: + { + break; + } + } + } } } diff --git a/odb/mssql/traits.hxx b/odb/mssql/traits.hxx index 28188cb..88703a1 100644 --- a/odb/mssql/traits.hxx +++ b/odb/mssql/traits.hxx @@ -11,8 +11,12 @@ #include <string> #include <vector> #include <cstddef> // std::size_t -//@@ #include <cstring> // std::memcpy, std::memset, std::strlen -//@@ #include <cassert> +#include <cstring> // std::memcpy, std::memset, std::strlen +#include <cwchar> // std::wcslen + +#ifdef _WIN32 +typedef struct _GUID GUID; +#endif #include <odb/traits.hxx> #include <odb/wrapper-traits.hxx> @@ -37,30 +41,30 @@ namespace odb id_int, id_bigint, - id_decimal, // DECIMAL; NUMERIC + id_decimal, // DECIMAL; NUMERIC id_smallmoney, id_money, - id_float4, // REAL; FLOAT(n) with n <= 24 - id_float8, // FLOAT(n) with n > 24 + id_float4, // REAL; FLOAT(n) with n <= 24 + id_float8, // FLOAT(n) with n > 24 - id_string, // CHAR(n), VARCHAR(n) with n <= N - id_long_string, // CHAR(n), VARCHAR(n) with n > N; TEXT + id_string, // CHAR(n), VARCHAR(n) with n <= N + id_long_string, // CHAR(n), VARCHAR(n) with n > N; TEXT - id_nstring, // NCHAR(n), NVARCHAR(n) with 2*n <= N - id_long_nstring, // NCHAR(n), NVARCHAR(n) with 2*n > N; NTEXT + id_nstring, // NCHAR(n), NVARCHAR(n) with 2*n <= N + id_long_nstring, // NCHAR(n), NVARCHAR(n) with 2*n > N; NTEXT - id_binary, // BINARY(n), VARBINARY(n) with n <= N - id_long_binary, // BINARY(n), VARBINARY(n) with n > N; IMAGE + id_binary, // BINARY(n), VARBINARY(n) with n <= N + id_long_binary, // BINARY(n), VARBINARY(n) with n > N; IMAGE - id_date, // DATE - id_time, // TIME - id_datetime, // DATETIME; DATETIME2; SMALLDATETIME - id_datetimeoffset, // DATETIMEOFFSET + id_date, // DATE + id_time, // TIME + id_datetime, // DATETIME; DATETIME2; SMALLDATETIME + id_datetimeoffset, // DATETIMEOFFSET - id_uuid, // UNIQUEIDENTIFIER - id_rowversion // ROWVERSION; TIMESTAMP + id_uniqueidentifier, // UNIQUEIDENTIFIER + id_rowversion // ROWVERSION; TIMESTAMP }; // @@ -128,12 +132,16 @@ namespace odb struct image_traits<id_datetime> {typedef datetime image_type;}; template <> - struct image_traits<id_datetimeoffset> {typedef datetimeoffset image_type;}; + struct image_traits<id_datetimeoffset> + { + typedef datetimeoffset image_type; + }; - // Image is a 16-byte sequence. - // template <> - struct image_traits<id_uuid> {typedef unsigned char* image_type;}; + struct image_traits<id_uniqueidentifier> + { + typedef uniqueidentifier image_type; + }; // Image is an 8-byte sequence. // @@ -200,10 +208,7 @@ namespace odb vtraits::set_image (i, is_null, wtraits::get_ref (v)); } - /* - @@ TODO - - // big_int, big_float, string, nstring, raw. + // string, binary. // static void set_value (W& v, const char* i, std::size_t n, bool is_null) @@ -211,8 +216,6 @@ namespace odb vtraits::set_value (wtraits::set_ref (v), i, n, is_null); } - // string, nstring, raw. - // static void set_image (char* i, std::size_t c, @@ -223,20 +226,30 @@ namespace odb vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v)); } - // big_int, big_float. + // nstring. // static void - set_image (char* i, std::size_t& n, bool& is_null, const W& v) + set_value (W& v, const ucs2_char* i, std::size_t n, bool is_null) + { + vtraits::set_value (wtraits::set_ref (v), i, n, is_null); + } + + static void + set_image (ucs2_char* i, + std::size_t c, + std::size_t& n, + bool& is_null, + const W& v) { - vtraits::set_image (i, n, is_null, wtraits::get_ref (v)); + vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v)); } - // blob, clob, nclob. + // long_string, long_nstring, long_binary. // static void - set_value (W& v, result_callback_type& cb, void*& context, bool is_null) + set_value (W& v, result_callback_type& cb, void*& context) { - vtraits::set_value (wtraits::set_ref (v), cb, context, is_null); + vtraits::set_value (wtraits::set_ref (v), cb, context); } static void @@ -247,7 +260,14 @@ namespace odb { vtraits::set_image (cb, context, is_null, wtraits::get_ref (v)); } - */ + + // time, datetime, datetimeoffset. + // + static void + set_image (image_type& i, unsigned short s, bool& is_null, const W& v) + { + vtraits::set_image (i, s, is_null, wtraits::get_ref (v)); + } }; template <typename W, database_type_id ID> @@ -280,10 +300,7 @@ namespace odb vtraits::set_image (i, is_null, wtraits::get_ref (v)); } - /* - @@ TODO - - // big_int, big_float, string, nstring, raw. + // string, binary. // static void set_value (W& v, const char* i, std::size_t n, bool is_null) @@ -294,8 +311,6 @@ namespace odb vtraits::set_value (wtraits::set_ref (v), i, n, is_null); } - // string, nstring, raw. - // static void set_image (char* i, std::size_t c, @@ -309,26 +324,40 @@ namespace odb vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v)); } - // big_int, big_float + // nstring. // static void - set_image (char* i, std::size_t& n, bool& is_null, const W& v) + set_value (W& v, const ucs2_char* i, std::size_t n, bool is_null) + { + if (is_null) + wtraits::set_null (v); + else + vtraits::set_value (wtraits::set_ref (v), i, n, is_null); + } + + static void + set_image (ucs2_char* i, + std::size_t c, + std::size_t& n, + bool& is_null, + const W& v) { is_null = wtraits::get_null (v); if (!is_null) - vtraits::set_image (i, n, is_null, wtraits::get_ref (v)); + vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v)); } - // blob, clob, nclob. + // long_string, long_nstring, long_binary. // static void - set_value (W& v, result_callback_type& cb, void*& context, bool is_null) + set_value (W& v, result_callback_type& cb, void*& context) { - if (is_null) - wtraits::set_null (v); - else - vtraits::set_value (wtraits::set_ref (v), cb, context, is_null); + // We have to use our own callback since the NULL information + // is only available during streaming. + // + cb = &result_callback; + context = &v; } static void @@ -342,7 +371,27 @@ namespace odb if (!is_null) vtraits::set_image (cb, context, is_null, wtraits::get_ref (v)); } - */ + + static void + result_callback (void* context, + std::size_t* position, + void** buffer, + std::size_t* size, + chunk_type chunk, + std::size_t size_left, + void* tmp_buffer, + std::size_t tmp_capacity); + + // time, datetime, datetimeoffset. + // + static void + set_image (image_type& i, unsigned short s, bool& is_null, const W& v) + { + is_null = wtraits::get_null (v); + + if (!is_null) + vtraits::set_image (i, s, is_null, wtraits::get_ref (v)); + } }; template <typename T, database_type_id ID> @@ -369,6 +418,939 @@ namespace odb } }; + // smallmoney as float/double. + // + template <typename T> + struct smallmoney_float_value_traits + { + typedef T value_type; + typedef T query_type; + typedef smallmoney image_type; + + static void + set_value (T& v, const smallmoney& i, bool is_null) + { + if (!is_null) + v = T (i.value / 10000) + T (i.value % 10000) / 10000; + else + v = T (); + } + + static void + set_image (smallmoney& i, bool& is_null, T v) + { + is_null = false; + i.value = static_cast<int> (v) * 10000 + + static_cast<int> (v * 10000) % 10000; + } + }; + + template <> + struct LIBODB_MSSQL_EXPORT default_value_traits<float, id_smallmoney>: + smallmoney_float_value_traits<float> + { + }; + + template <> + struct LIBODB_MSSQL_EXPORT default_value_traits<double, id_smallmoney>: + smallmoney_float_value_traits<double> + { + }; + + // smallmoney as integer. + // + template <typename T> + struct default_value_traits<T, id_smallmoney> + { + typedef T value_type; + typedef T query_type; + typedef smallmoney image_type; + + static void + set_value (T& v, const smallmoney& i, bool is_null) + { + if (!is_null) + v = static_cast<T> (i.value); + else + v = T (); + } + + static void + set_image (smallmoney& i, bool& is_null, T v) + { + is_null = false; + i.value = static_cast<int> (v); + } + }; + + // money as float/double. + // + template <typename T> + struct money_float_value_traits + { + typedef T value_type; + typedef T query_type; + typedef money image_type; + + static void + set_value (T& v, const money& i, bool is_null) + { + if (!is_null) + { + long long iv ((static_cast<long long> (i.high) << 32) | i.low); + v = T (iv / 10000) + T (iv % 10000) / 10000; + } + else + v = T (); + } + + static void + set_image (money& i, bool& is_null, T v) + { + is_null = false; + long long iv (static_cast<long long> (v) * 10000 + + static_cast<long long> (v * 10000) % 10000); + i.high = static_cast<int> (iv >> 32); + i.low = static_cast<unsigned int> (iv); + } + }; + + template <> + struct LIBODB_MSSQL_EXPORT default_value_traits<float, id_money>: + money_float_value_traits<float> + { + }; + + template <> + struct LIBODB_MSSQL_EXPORT default_value_traits<double, id_money>: + money_float_value_traits<double> + { + }; + + // money as integer. + // + template <typename T> + struct default_value_traits<T, id_money> + { + typedef T value_type; + typedef T query_type; + typedef money image_type; + + static void + set_value (T& v, const money& i, bool is_null) + { + if (!is_null) + { + long long iv ((static_cast<long long> (i.high) << 32) | i.low); + v = static_cast<T> (iv); + } + else + v = T (); + } + + static void + set_image (money& i, bool& is_null, T v) + { + is_null = false; + long long iv (static_cast<long long> (v)); + i.high = static_cast<int> (iv >> 32); + i.low = static_cast<unsigned int> (iv); + } + }; + + // std::string specialization for string. + // + template <> + struct LIBODB_MSSQL_EXPORT default_value_traits<std::string, id_string> + { + typedef std::string value_type; + typedef std::string query_type; + typedef char* image_type; + + static void + set_value (std::string& v, + const char* b, + std::size_t n, + bool is_null) + { + if (!is_null) + v.assign (b, n); + else + v.erase (); + } + + static void + set_image (char* b, + std::size_t c, + std::size_t& n, + bool& is_null, + const std::string& v) + { + is_null = false; + n = v.size (); + + if (n > c) + n = c; + + if (n != 0) + std::memcpy (b, v.c_str (), n); + } + }; + + // const char* specialization for string. + // + // Specialization for const char* which only supports initialization + // of an image from the value but not the other way around. This way + // we can pass such values to the queries. + // + class LIBODB_MSSQL_EXPORT c_string_value_traits + { + public: + typedef const char* value_type; + typedef const char* query_type; + typedef char* image_type; + + static void + set_image (char* b, + std::size_t c, + std::size_t& n, + bool& is_null, + const char* v) + { + is_null = false; + n = std::strlen (v); + + if (n > c) + n = c; + + if (n != 0) + std::memcpy (b, v, n); + } + }; + + template <> + struct LIBODB_MSSQL_EXPORT default_value_traits<const char*, id_string>: + c_string_value_traits + { + }; + + template <std::size_t n> + struct default_value_traits<char[n], id_string>: c_string_value_traits + { + }; + + template <std::size_t n> + struct default_value_traits<const char[n], id_string>: + c_string_value_traits + { + }; + + // std::string specialization for long_string. + // + template <> + struct LIBODB_MSSQL_EXPORT default_value_traits<std::string, + id_long_string> + { + typedef std::string value_type; + typedef std::string query_type; + typedef long_callback image_type; + + static void + set_value (std::string& v, + result_callback_type& cb, + void*& context) + { + cb = &result_callback; + context = &v; + } + + static void + set_image (param_callback_type& cb, + const void*& context, + bool& is_null, + const std::string& v) + { + is_null = false; + cb = ¶m_callback; + context = &v; + } + + static void + param_callback (const void* context, + std::size_t* position, + const void** buffer, + std::size_t* size, + chunk_type* chunk, + void* tmp_buffer, + std::size_t tmp_capacity); + + static void + result_callback (void* context, + std::size_t* position, + void** buffer, + std::size_t* size, + chunk_type chunk, + std::size_t size_left, + void* tmp_buffer, + std::size_t tmp_capacity); + }; + + // const char* specialization for long_string. + // + class LIBODB_MSSQL_EXPORT c_string_long_value_traits + { + public: + typedef const char* value_type; + typedef const char* query_type; + typedef long_callback image_type; + + static void + set_image (param_callback_type& cb, + const void*& context, + bool& is_null, + const char* v) + { + is_null = false; + cb = ¶m_callback; + context = v; + } + + static void + param_callback (const void* context, + std::size_t* position, + const void** buffer, + std::size_t* size, + chunk_type* chunk, + void* tmp_buffer, + std::size_t tmp_capacity); + }; + + template <> + struct LIBODB_MSSQL_EXPORT default_value_traits<const char*, + id_long_string>: + c_string_long_value_traits + { + }; + + template <std::size_t n> + struct default_value_traits<char[n], id_long_string>: + c_string_long_value_traits + { + }; + + template <std::size_t n> + struct default_value_traits<const char[n], id_long_string>: + c_string_long_value_traits + { + }; + + // std::wstring specialization for nstring. + // + template <std::size_t = sizeof (wchar_t)> + struct wstring_functions + { + static void + assign (std::wstring& s, const ucs2_char* b, std::size_t n) + { + s.assign (b, b + n); + } + + static void + assign (ucs2_char* b, const wchar_t* s, std::size_t n) + { + for (std::size_t i (0); i < n; ++i) + b[i] = static_cast<ucs2_char> (s[i]); + } + }; + + template <> + struct LIBODB_MSSQL_EXPORT wstring_functions<sizeof (ucs2_char)> + { + static void + assign (std::wstring& s, const ucs2_char* b, std::size_t n) + { + s.assign (reinterpret_cast<const wchar_t*> (b), n); + } + + static void + assign (ucs2_char* b, const wchar_t* s, std::size_t n) + { + if (n != 0) + std::memcpy (b, s, n * sizeof (ucs2_char)); + } + }; + + template <> + struct LIBODB_MSSQL_EXPORT default_value_traits<std::wstring, id_nstring> + { + typedef std::wstring value_type; + typedef std::wstring query_type; + typedef ucs2_char* image_type; + + typedef wstring_functions<> functions; + + static void + set_value (std::wstring& v, + const ucs2_char* b, + std::size_t n, + bool is_null) + { + if (!is_null) + functions::assign (v, b, n); + else + v.erase (); + } + + static void + set_image (ucs2_char* b, + std::size_t c, + std::size_t& n, + bool& is_null, + const std::wstring& v) + { + is_null = false; + n = v.size (); + + if (n > c) + n = c; + + functions::assign (b, v.c_str (), n); + } + }; + + // const wchar_t* specialization for nstring. + // + class LIBODB_MSSQL_EXPORT c_wstring_value_traits + { + public: + typedef const wchar_t* value_type; + typedef const wchar_t* query_type; + typedef ucs2_char* image_type; + + typedef wstring_functions<> functions; + + static void + set_image (ucs2_char* b, + std::size_t c, + std::size_t& n, + bool& is_null, + const wchar_t* v) + { + is_null = false; + n = std::wcslen (v); + + if (n > c) + n = c; + + functions::assign (b, v, n); + } + }; + + template <> + struct LIBODB_MSSQL_EXPORT default_value_traits<const wchar_t*, + id_nstring>: + c_wstring_value_traits + { + }; + + template <std::size_t n> + struct default_value_traits<wchar_t[n], id_nstring>: + c_wstring_value_traits + { + }; + + template <std::size_t n> + struct default_value_traits<const wchar_t[n], id_nstring>: + c_wstring_value_traits + { + }; + + // std::wstring specialization for long_nstring. + // + template <std::size_t = sizeof (wchar_t)> + struct wstring_long_value_traits; + + template <> + struct LIBODB_MSSQL_EXPORT wstring_long_value_traits<2> + { + static void + param_callback (const void* context, + std::size_t* position, + const void** buffer, + std::size_t* size, + chunk_type* chunk, + void* tmp_buffer, + std::size_t tmp_capacity); + + static void + result_callback (void* context, + std::size_t* position, + void** buffer, + std::size_t* size, + chunk_type chunk, + std::size_t size_left, + void* tmp_buffer, + std::size_t tmp_capacity); + }; + +#ifndef _WIN32 + template <> + struct LIBODB_MSSQL_EXPORT wstring_long_value_traits<4> + { + static void + param_callback (const void* context, + std::size_t* position, + const void** buffer, + std::size_t* size, + chunk_type* chunk, + void* tmp_buffer, + std::size_t tmp_capacity); + + static void + result_callback (void* context, + std::size_t* position, + void** buffer, + std::size_t* size, + chunk_type chunk, + std::size_t size_left, + void* tmp_buffer, + std::size_t tmp_capacity); + }; +#endif + + template <> + struct LIBODB_MSSQL_EXPORT default_value_traits<std::wstring, + id_long_nstring> + { + typedef std::wstring value_type; + typedef std::wstring query_type; + typedef long_callback image_type; + + static void + set_value (std::wstring& v, + result_callback_type& cb, + void*& context) + { + cb = &wstring_long_value_traits<>::result_callback; + context = &v; + } + + static void + set_image (param_callback_type& cb, + const void*& context, + bool& is_null, + const std::wstring& v) + { + is_null = false; + cb = &wstring_long_value_traits<>::param_callback; + context = &v; + } + }; + + // const wchar_t* specialization for long_nstring. + // + template <std::size_t = sizeof (wchar_t)> + struct c_wstring_long_value_traits; + + template <> + struct LIBODB_MSSQL_EXPORT c_wstring_long_value_traits<2> + { + typedef const wchar_t* value_type; + typedef const wchar_t* query_type; + typedef long_callback image_type; + + static void + set_image (param_callback_type& cb, + const void*& context, + bool& is_null, + const wchar_t* v) + { + is_null = false; + cb = ¶m_callback; + context = v; + } + + static void + param_callback (const void* context, + std::size_t* position, + const void** buffer, + std::size_t* size, + chunk_type* chunk, + void* tmp_buffer, + std::size_t tmp_capacity); + }; + +#ifndef _WIN32 + template <> + struct LIBODB_MSSQL_EXPORT c_wstring_long_value_traits<4> + { + typedef const wchar_t* value_type; + typedef const wchar_t* query_type; + typedef long_callback image_type; + + static void + set_image (param_callback_type& cb, + const void*& context, + bool& is_null, + const wchar_t* v) + { + is_null = false; + cb = ¶m_callback; + context = v; + } + + static void + param_callback (const void* context, + std::size_t* position, + const void** buffer, + std::size_t* size, + chunk_type* chunk, + void* tmp_buffer, + std::size_t tmp_capacity); + }; +#endif + + template <> + struct LIBODB_MSSQL_EXPORT default_value_traits<const wchar_t*, + id_long_nstring>: + c_wstring_long_value_traits<> + { + }; + + template <std::size_t n> + struct default_value_traits<wchar_t[n], id_long_nstring>: + c_wstring_long_value_traits<> + { + }; + + template <std::size_t n> + struct default_value_traits<const wchar_t[n], id_long_nstring>: + c_wstring_long_value_traits<> + { + }; + + // std::vector (buffer) specialization for binary. + // + template <typename C> + struct vector_binary_value_traits + { + typedef std::vector<C> value_type; + typedef std::vector<C> query_type; + typedef char* image_type; + + static void + set_value (value_type& v, const char* b, std::size_t n, bool is_null) + { + if (!is_null) + { + const C* cb (reinterpret_cast<const C*> (b)); + v.assign (cb, cb + n); + } + else + v.clear (); + } + + static void + set_image (char* b, + std::size_t c, + std::size_t& n, + bool& is_null, + const value_type& v) + { + is_null = false; + n = v.size (); + + if (n > c) + n = c; + + // std::vector::data() may not be available in older compilers. + // + if (n != 0) + std::memcpy (b, &v.front (), n); + } + }; + + template <> + struct LIBODB_MSSQL_EXPORT default_value_traits<std::vector<char>, + id_binary>: + vector_binary_value_traits<char> + { + }; + + template <> + struct LIBODB_MSSQL_EXPORT default_value_traits<std::vector<unsigned char>, + id_binary>: + vector_binary_value_traits<unsigned char> + { + }; + + // char array (buffer) specialization for binary. + // + template <typename C, std::size_t N> + struct array_binary_value_traits + { + typedef C* value_type; + typedef const C* query_type; + typedef char* image_type; + + static void + set_value (C* const& v, const char* b, std::size_t n, bool is_null) + { + if (!is_null) + std::memcpy (v, b, n < N ? n : N); + else + std::memset (v, 0, N); + } + + static void + set_image (char* b, + std::size_t c, + std::size_t& n, + bool& is_null, + const C* v) + { + is_null = false; + n = c > N ? N : c; + std::memcpy (b, v, n); + } + }; + + template <std::size_t N> + struct default_value_traits<char[N], id_binary>: + array_binary_value_traits<char, N> + { + }; + + template <std::size_t N> + struct default_value_traits<unsigned char[N], id_binary>: + array_binary_value_traits<unsigned char, N> + { + }; + + // std::vector<char> (buffer) specialization for long_binary. + // + template <> + struct LIBODB_MSSQL_EXPORT default_value_traits<std::vector<char>, + id_long_binary> + { + typedef std::vector<char> value_type; + typedef std::vector<char> query_type; + typedef long_callback image_type; + + static void + set_value (value_type& v, + result_callback_type& cb, + void*& context) + { + cb = &result_callback; + context = &v; + } + + static void + set_image (param_callback_type& cb, + const void*& context, + bool& is_null, + const value_type& v) + { + is_null = false; + cb = ¶m_callback; + context = &v; + } + + static void + param_callback (const void* context, + std::size_t* position, + const void** buffer, + std::size_t* size, + chunk_type* chunk, + void* tmp_buffer, + std::size_t tmp_capacity); + + static void + result_callback (void* context, + std::size_t* position, + void** buffer, + std::size_t* size, + chunk_type chunk, + std::size_t size_left, + void* tmp_buffer, + std::size_t tmp_capacity); + }; + + // std::vector<unsigned char> (buffer) specialization for long_binary. + // + template <> + struct LIBODB_MSSQL_EXPORT default_value_traits<std::vector<unsigned char>, + id_long_binary> + { + typedef std::vector<unsigned char> value_type; + typedef std::vector<unsigned char> query_type; + typedef long_callback image_type; + + static void + set_value (value_type& v, + result_callback_type& cb, + void*& context) + { + cb = &result_callback; + context = &v; + } + + static void + set_image (param_callback_type& cb, + const void*& context, + bool& is_null, + const value_type& v) + { + is_null = false; + cb = ¶m_callback; + context = &v; + } + + static void + param_callback (const void* context, + std::size_t* position, + const void** buffer, + std::size_t* size, + chunk_type* chunk, + void* tmp_buffer, + std::size_t tmp_capacity); + + static void + result_callback (void* context, + std::size_t* position, + void** buffer, + std::size_t* size, + chunk_type chunk, + std::size_t size_left, + void* tmp_buffer, + std::size_t tmp_capacity); + }; + + // char array (buffer) specialization for long_binary. + // + template <typename C, std::size_t N> + struct array_long_binary_value_traits + { + typedef C* value_type; + typedef const C* query_type; + typedef long_callback image_type; + + static void + set_value (C* const& v, + result_callback_type& cb, + void*& context) + { + cb = &result_callback; + context = v; + } + + static void + set_image (param_callback_type& cb, + const void*& context, + bool& is_null, + const C* v) + { + is_null = false; + cb = ¶m_callback; + context = v; + } + + static void + param_callback (const void* context, + std::size_t* position, + const void** buffer, + std::size_t* size, + chunk_type* chunk, + void* tmp_buffer, + std::size_t tmp_capacity); + + static void + result_callback (void* context, + std::size_t* position, + void** buffer, + std::size_t* size, + chunk_type chunk, + std::size_t size_left, + void* tmp_buffer, + std::size_t tmp_capacity); + }; + + template <std::size_t N> + struct default_value_traits<char[N], id_long_binary>: + array_long_binary_value_traits<char, N> + { + }; + + template <std::size_t N> + struct default_value_traits<unsigned char[N], id_long_binary>: + array_long_binary_value_traits<unsigned char, N> + { + }; + + // GUID specialization for uniqueidentifier. + // +#ifdef _WIN32 + template <> + struct LIBODB_MSSQL_EXPORT default_value_traits<GUID, id_uniqueidentifier> + { + typedef GUID value_type; + typedef GUID query_type; + typedef uniqueidentifier image_type; + + static void + set_value (GUID& v, const uniqueidentifier& i, bool is_null) + { + if (!is_null) + std::memcpy (&v, &i, 16); + else + std::memset (&v, 0, 16); + } + + static void + set_image (uniqueidentifier& i, bool& is_null, const GUID& v) + { + is_null = false; + std::memcpy (&i, &v, 16); + } + }; +#endif + + // unsigned long long specialization for rowversion. + // + template <> + struct LIBODB_MSSQL_EXPORT default_value_traits<unsigned long long, + id_rowversion> + { + typedef unsigned long long value_type; + typedef unsigned long long query_type; + typedef unsigned char* image_type; + + static void + set_value (unsigned long long& v, const unsigned char* i, bool is_null) + { + if (!is_null) + { + // The value is in the big-endian format. + // + unsigned char* p (reinterpret_cast<unsigned char*> (&v)); + p[0] = i[7]; + p[1] = i[6]; + p[2] = i[5]; + p[3] = i[4]; + p[4] = i[3]; + p[5] = i[2]; + p[6] = i[1]; + p[7] = i[0]; + } + else + v = 0; + } + + // There is no set_image() since it is impossible to insert an + // explicit value into a rowversion column. + }; + // // type_traits // @@ -488,6 +1470,42 @@ namespace odb { static const database_type_id db_type_id = id_long_string; }; + + // Wide string type. + // + template <> + struct default_type_traits<std::wstring> + { + static const database_type_id db_type_id = id_long_nstring; + }; + + template <> + struct default_type_traits<const wchar_t*> + { + static const database_type_id db_type_id = id_long_nstring; + }; + + template <std::size_t n> + struct default_type_traits<wchar_t[n]> + { + static const database_type_id db_type_id = id_long_nstring; + }; + + template <std::size_t n> + struct default_type_traits<const wchar_t[n]> + { + static const database_type_id db_type_id = id_long_nstring; + }; + + // GUID. + // +#ifdef _WIN32 + template <> + struct default_type_traits<GUID> + { + static const database_type_id db_type_id = id_uniqueidentifier; + }; +#endif } } diff --git a/odb/mssql/traits.txx b/odb/mssql/traits.txx index b3bc442..80ee933 100644 --- a/odb/mssql/traits.txx +++ b/odb/mssql/traits.txx @@ -3,9 +3,118 @@ // copyright : Copyright (c) 2005-2011 Code Synthesis Tools CC // license : ODB NCUEL; see accompanying LICENSE file +#include <cassert> + namespace odb { namespace mssql { + // + // wrapped_value_traits<W, ID, true> + // + + template <typename W, database_type_id ID> + void wrapped_value_traits<W, ID, true>:: + result_callback (void* context, + std::size_t* position, + void** buffer, + std::size_t* size, + chunk_type chunk, + std::size_t size_left, + void* tmp_buffer, + std::size_t tmp_capacity) + { + W& v (*static_cast<W*> (context)); + + if (chunk == chunk_null) + wtraits::set_null (v); + else + { + long_callback& c (*static_cast<long_callback*> (*buffer)); + + // Redirect all further calls. + // + vtraits::set_value (wtraits::set_ref (v), + c.callback.result, + c.context.result); + + // Forward this call. + // + c.callback.result ( + c.context.result, + position, + buffer, + size, + chunk, + size_left, + tmp_buffer, + tmp_capacity); + } + } + + // + // array_long_binary_value_traits + // + + template <typename C, std::size_t N> + void array_long_binary_value_traits<C, N>:: + param_callback (const void* context, + std::size_t*, + const void** buffer, + std::size_t* size, + chunk_type* chunk, + void*, + std::size_t) + { + *buffer = context; + *size = N; + *chunk = chunk_one; + } + + template <typename C, std::size_t N> + void array_long_binary_value_traits<C, N>:: + result_callback (void* context, + std::size_t*, + void** buffer, + std::size_t* size, + chunk_type chunk, + std::size_t size_left, + void* tmp_buf, + std::size_t tmp_capacity) + { + // The code is similar to the vector<char> specialization. + // + switch (chunk) + { + case chunk_null: + case chunk_one: + { + std::memset (context, 0, N); + break; + } + case chunk_first: + { + assert (size_left != 0); + + *buffer = context; + *size = size_left < N ? size_left : N; + 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. + // + *buffer = tmp_buf; + *size = tmp_capacity; + break; + } + case chunk_last: + { + break; + } + } + } } } |