aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2011-12-21 11:19:24 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2011-12-21 11:19:24 +0200
commit28b329b63a3919020a9286b557f7938dc8fbd746 (patch)
tree45ff48d166a6dd4f02f710fe96d34fd10b4003e9
parent213740c161499a5961fa426d1b77c7d68763c5df (diff)
ODB compiler implementation, traits, and types test for SQL Server
-rw-r--r--odb/mssql/mssql-fwd.hxx72
-rw-r--r--odb/mssql/mssql-types.hxx103
-rw-r--r--odb/mssql/mssql.hxx5
-rw-r--r--odb/mssql/statement.cxx307
-rw-r--r--odb/mssql/traits.cxx515
-rw-r--r--odb/mssql/traits.hxx1118
-rw-r--r--odb/mssql/traits.txx109
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 = &param_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 = &param_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 = &param_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 = &param_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 = &param_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 = &param_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 = &param_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;
+ }
+ }
+ }
}
}