aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2011-11-28 15:08:56 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2011-11-28 15:08:56 +0200
commit91c962e4615101e14be4c720fc386878ddb598a4 (patch)
tree9c62ace7ee457f9e7c1edd9c89a5b6fba274d363
parent8823eb1c28f6589068a080a68283a8ddb47cb71b (diff)
Implement statements; add support for tracing
-rw-r--r--odb/mssql/auto-handle.hxx2
-rw-r--r--odb/mssql/binding.hxx45
-rw-r--r--odb/mssql/connection.cxx21
-rw-r--r--odb/mssql/connection.hxx15
-rw-r--r--odb/mssql/database.hxx8
-rw-r--r--odb/mssql/error.cxx3
-rw-r--r--odb/mssql/error.hxx2
-rw-r--r--odb/mssql/exceptions.hxx2
-rw-r--r--odb/mssql/makefile2
-rw-r--r--odb/mssql/mssql-fwd.hxx73
-rw-r--r--odb/mssql/mssql-types.hxx133
-rw-r--r--odb/mssql/mssql.hxx32
-rw-r--r--odb/mssql/statement.cxx805
-rw-r--r--odb/mssql/statement.hxx228
-rw-r--r--odb/mssql/tracer.cxx62
-rw-r--r--odb/mssql/tracer.hxx63
-rw-r--r--odb/mssql/transaction-impl.cxx9
-rw-r--r--odb/mssql/transaction.hxx6
18 files changed, 1465 insertions, 46 deletions
diff --git a/odb/mssql/auto-handle.hxx b/odb/mssql/auto-handle.hxx
index d545629..56f2fc2 100644
--- a/odb/mssql/auto-handle.hxx
+++ b/odb/mssql/auto-handle.hxx
@@ -8,8 +8,8 @@
#include <odb/pre.hxx>
-#include <odb/mssql/version.hxx>
#include <odb/mssql/mssql-fwd.hxx>
+#include <odb/mssql/version.hxx>
#include <odb/mssql/details/export.hxx>
diff --git a/odb/mssql/binding.hxx b/odb/mssql/binding.hxx
new file mode 100644
index 0000000..f8f4fc4
--- /dev/null
+++ b/odb/mssql/binding.hxx
@@ -0,0 +1,45 @@
+// file : odb/mssql/binding.hxx
+// author : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2005-2011 Code Synthesis Tools CC
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_BINDING_HXX
+#define ODB_MSSQL_BINDING_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/mssql-types.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class LIBODB_MSSQL_EXPORT binding
+ {
+ public:
+ typedef mssql::bind bind_type;
+
+ binding (bind_type* b, std::size_t n)
+ : bind (b), count (n), version (0)
+ {
+ }
+
+ bind_type* bind;
+ std::size_t count;
+ std::size_t version;
+
+ private:
+ binding (const binding&);
+ binding& operator= (const binding&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_BINDING_HXX
diff --git a/odb/mssql/connection.cxx b/odb/mssql/connection.cxx
index 3ba10ba..1609b80 100644
--- a/odb/mssql/connection.cxx
+++ b/odb/mssql/connection.cxx
@@ -25,8 +25,9 @@ namespace odb
connection (database_type& db)
: odb::connection (db),
db_ (db),
- state_ (state_disconnected)
+ state_ (state_disconnected),
// statement_cache_ (new statement_cache_type (*this))
+ long_buffer_ (0)
{
SQLRETURN r;
@@ -46,7 +47,7 @@ namespace odb
//
r = SQLSetConnectAttr (handle_,
SQL_ATTR_AUTOCOMMIT,
- SQL_AUTOCOMMIT_OFF,
+ (SQLPOINTER) SQL_AUTOCOMMIT_OFF,
0);
if (!SQL_SUCCEEDED (r))
@@ -55,6 +56,16 @@ namespace odb
//
translate_error (r, handle_, SQL_HANDLE_DBC);
+ // Enable Multiple Active Result Sets (MARS).
+ //
+ r = SQLSetConnectAttr (handle_,
+ SQL_COPT_SS_MARS_ENABLED,
+ (SQLPOINTER) SQL_MARS_ENABLED_YES,
+ SQL_IS_UINTEGER);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, handle_, SQL_HANDLE_DBC);
+
// Connect.
//
{
@@ -84,8 +95,9 @@ namespace odb
: odb::connection (db),
db_ (db),
handle_ (handle),
- state_ (state_connected)
+ state_ (state_connected),
// statement_cache_ (new statement_cache_type (*this))
+ long_buffer_ (0)
{
}
@@ -109,8 +121,6 @@ namespace odb
unsigned long long connection::
execute (const char* s, std::size_t n)
{
- /*
- @@
{
odb::tracer* t;
if ((t = transaction_tracer ()) ||
@@ -121,7 +131,6 @@ namespace odb
t->execute (*this, str.c_str ());
}
}
- */
SQLRETURN r;
diff --git a/odb/mssql/connection.hxx b/odb/mssql/connection.hxx
index 1a6cd24..c38692b 100644
--- a/odb/mssql/connection.hxx
+++ b/odb/mssql/connection.hxx
@@ -15,14 +15,15 @@
#include <odb/forward.hxx>
#include <odb/connection.hxx>
+#include <odb/details/buffer.hxx>
#include <odb/details/shared-ptr.hxx>
+#include <odb/mssql/mssql-fwd.hxx>
#include <odb/mssql/version.hxx>
#include <odb/mssql/forward.hxx>
-//#include <odb/mssql/tracer.hxx>
+#include <odb/mssql/tracer.hxx>
#include <odb/mssql/transaction-impl.hxx>
#include <odb/mssql/auto-handle.hxx>
-#include <odb/mssql/mssql-fwd.hxx>
#include <odb/mssql/details/export.hxx>
@@ -66,7 +67,6 @@ namespace odb
// SQL statement tracing.
//
public:
- /*
typedef mssql::tracer tracer_type;
void
@@ -82,7 +82,6 @@ namespace odb
}
using odb::connection::tracer;
- */
public:
bool
@@ -112,6 +111,12 @@ namespace odb
}
*/
+ details::buffer&
+ long_buffer ()
+ {
+ return long_buffer_;
+ }
+
private:
connection (const connection&);
connection& operator= (const connection&);
@@ -133,6 +138,8 @@ namespace odb
auto_handle<SQL_HANDLE_STMT> direct_stmt_;
//std::auto_ptr<statement_cache_type> statement_cache_;
+
+ details::buffer long_buffer_;
};
}
}
diff --git a/odb/mssql/database.hxx b/odb/mssql/database.hxx
index cabdbb4..5bb5e4f 100644
--- a/odb/mssql/database.hxx
+++ b/odb/mssql/database.hxx
@@ -6,8 +6,6 @@
#ifndef ODB_MSSQL_DATABASE_HXX
#define ODB_MSSQL_DATABASE_HXX
-//@@ disabled functionality
-
#include <odb/pre.hxx>
#include <string>
@@ -16,13 +14,13 @@
#include <odb/database.hxx>
+#include <odb/mssql/mssql-fwd.hxx>
#include <odb/mssql/version.hxx>
#include <odb/mssql/forward.hxx>
-//#include <odb/mssql/tracer.hxx>
+#include <odb/mssql/tracer.hxx>
#include <odb/mssql/connection.hxx>
#include <odb/mssql/connection-factory.hxx>
#include <odb/mssql/auto-handle.hxx>
-#include <odb/mssql/mssql-fwd.hxx>
#include <odb/mssql/details/export.hxx>
@@ -211,7 +209,6 @@ namespace odb
// SQL statement tracing.
//
public:
- /*
typedef mssql::tracer tracer_type;
void
@@ -227,7 +224,6 @@ namespace odb
}
using odb::database::tracer;
- */
public:
virtual
diff --git a/odb/mssql/error.cxx b/odb/mssql/error.cxx
index dffe4cf..613f346 100644
--- a/odb/mssql/error.cxx
+++ b/odb/mssql/error.cxx
@@ -87,13 +87,12 @@ namespace odb
0,
&msg_size);
- string s (sqlstate);
-
if (r == SQL_NO_DATA)
break;
else if (SQL_SUCCEEDED (r))
{
code nc;
+ string s (sqlstate);
if (s == "40001") // Serialization failure (native code 1205).
nc = code_deadlock;
diff --git a/odb/mssql/error.hxx b/odb/mssql/error.hxx
index 126f7d9..fd7cfb6 100644
--- a/odb/mssql/error.hxx
+++ b/odb/mssql/error.hxx
@@ -8,9 +8,9 @@
#include <odb/pre.hxx>
+#include <odb/mssql/mssql-fwd.hxx>
#include <odb/mssql/version.hxx>
#include <odb/mssql/forward.hxx> // connection
-#include <odb/mssql/mssql-fwd.hxx>
#include <odb/mssql/auto-handle.hxx>
#include <odb/mssql/details/export.hxx>
diff --git a/odb/mssql/exceptions.hxx b/odb/mssql/exceptions.hxx
index 321a41d..3e7a845 100644
--- a/odb/mssql/exceptions.hxx
+++ b/odb/mssql/exceptions.hxx
@@ -13,8 +13,8 @@
#include <odb/exceptions.hxx>
-#include <odb/mssql/version.hxx>
#include <odb/mssql/mssql-fwd.hxx>
+#include <odb/mssql/version.hxx>
#include <odb/mssql/details/export.hxx>
namespace odb
diff --git a/odb/mssql/makefile b/odb/mssql/makefile
index 63aa888..4758abf 100644
--- a/odb/mssql/makefile
+++ b/odb/mssql/makefile
@@ -13,6 +13,8 @@ connection-factory.cxx \
database.cxx \
error.cxx \
exceptions.cxx \
+statement.cxx \
+tracer.cxx \
transaction.cxx \
transaction-impl.cxx
diff --git a/odb/mssql/mssql-fwd.hxx b/odb/mssql/mssql-fwd.hxx
index b4afbb6..3579403 100644
--- a/odb/mssql/mssql-fwd.hxx
+++ b/odb/mssql/mssql-fwd.hxx
@@ -8,25 +8,78 @@
#include <odb/pre.hxx>
+#include <cstddef> // std::size_t
+
// Forward declaration for some of the types defined in sqltypes.h or
// sqlncli.h. This allows us to avoid having to include these files
// in public headers.
//
#ifdef _WIN32
-typedef long SQLINTEGER;
-typedef unsigned long SQLUINTEGER;
+
+// Keep consistent with Windows ODBC headers.
+//
+
+typedef long SQLINTEGER;
+typedef unsigned long SQLUINTEGER;
+
+#ifdef _WIN64
+typedef __int64 SQLLEN;
+typedef unsigned __int64 SQLULEN;
+#else
+#ifndef SQLLEN
+typedef SQLINTEGER SQLLEN;
+typedef SQLUINTEGER SQLULEN;
+#endif
+#endif
+
+#else // _WIN32
+
+// Keep consistent with unixODBC headers.
+//
+
+template <std::size_t sizeof_long>
+struct odbc_types;
+
+template <>
+struct odbc_types<4>
+{
+ typedef long integer;
+ typedef unsigned long uinteger;
+
+ typedef integer len;
+ typedef uinteger ulen;
+};
+
+template <>
+struct odbc_types<8>
+{
+ typedef int integer;
+ typedef unsigned int uinteger;
+
+ typedef long len;
+ typedef unsigned long ulen;
+};
+
+typedef odbc_types<sizeof (long)>::integer SQLINTEGER;
+typedef odbc_types<sizeof (long)>::uinteger SQLUINTEGER;
+
+#ifndef SQLLEN
+typedef odbc_types<sizeof (long)>::len SQLLEN;
+typedef odbc_types<sizeof (long)>::ulen SQLULEN;
#endif
-typedef short SQLSMALLINT;
-typedef unsigned short SQLUSMALLINT;
+#endif // _WIN32
+
+typedef short SQLSMALLINT;
+typedef unsigned short SQLUSMALLINT;
-typedef SQLSMALLINT SQLRETURN;
+typedef SQLSMALLINT SQLRETURN;
-typedef void* SQLHANDLE;
-typedef SQLHANDLE SQLHENV;
-typedef SQLHANDLE SQLHDBC;
-typedef SQLHANDLE SQLHSTMT;
-typedef SQLHANDLE SQLHDESC;
+typedef void* SQLHANDLE;
+typedef SQLHANDLE SQLHENV;
+typedef SQLHANDLE SQLHDBC;
+typedef SQLHANDLE SQLHSTMT;
+typedef SQLHANDLE SQLHDESC;
// If you get a redefinition error or warning for one of these macros,
// then that means you included this header (or one that includes it),
diff --git a/odb/mssql/mssql-types.hxx b/odb/mssql/mssql-types.hxx
new file mode 100644
index 0000000..2ce7940
--- /dev/null
+++ b/odb/mssql/mssql-types.hxx
@@ -0,0 +1,133 @@
+// file : odb/mssql/mssql-types.hxx
+// author : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2005-2011 Code Synthesis Tools CC
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_MSSQL_TYPES_HXX
+#define ODB_MSSQL_MSSQL_TYPES_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/mssql/mssql-fwd.hxx>
+#include <odb/mssql/version.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ enum chunk_type
+ {
+ first_chunk,
+ next_chunk,
+ last_chunk,
+ one_chunk,
+ null_chunk
+ };
+
+ typedef void (*param_callback_type) (
+ const void* context, // User context.
+ std::size_t* position, // Position context. Am 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.
+ 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
+ // implementation.
+ std::size_t capacity); // Capacity of the temporary buffer.
+
+ typedef void (*result_callback_type) (
+ void* context, // User context.
+ std::size_t* position, // Position context. Am 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.
+ 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).
+ 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
+ // implementation.
+ std::size_t capacity); // Capacity of the temporary buffer.
+
+ struct long_callback
+ {
+ union
+ {
+ param_callback_type param;
+ result_callback_type result;
+ } callback;
+
+ union
+ {
+ const void* param;
+ void* result;
+ } context;
+ };
+
+ struct bind
+ {
+ // This enumeration identifies the possible buffer types that can be
+ // bound to a parameter or result. In most cases, these map directly
+ // to SQL_XXX/SQL_C_XXX codes.
+ //
+ 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.
+
+ /*
+ numeric, // Buffer is an SQL_NUMERIC_STRUCT.
+
+ smallmoney, // Buffer is a 4-byte integer (*10,000 value).
+ money, // Buffer is an 8-byte integer (*10,000 value).
+ */
+
+ float4, // Buffer is a float.
+ float8, // Buffer is a double.
+
+ 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.
+
+ 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.
+
+ uuid, // Buffer is a 16-byte array (or SQLGUID).
+ rowversion, // Buffer is an 8-byte array.
+ */
+
+ 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.
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_MSSQL_TYPES_HXX
diff --git a/odb/mssql/mssql.hxx b/odb/mssql/mssql.hxx
index 80cff65..8c455ba 100644
--- a/odb/mssql/mssql.hxx
+++ b/odb/mssql/mssql.hxx
@@ -24,7 +24,37 @@
#include <sqlext.h> // Standard ODBC.
//#define _SQLNCLI_ODBC_
-//#include <sqlncli.h> // SQL Server native client driver specifics.
+//#include <sqlncli.h> // SQL Server Native Client driver specifics.
+
+// Instead of having a dependency on <sqlncli.h> (which, BTW, is not
+// currently available for the Linux version of the Native Client),
+// we are going to provide the few definitions that we need ourselves.
+//
+#ifndef SQL_SS_LENGTH_UNLIMITED
+# define SQL_SS_LENGTH_UNLIMITED 0
+#endif
+
+#ifndef SQL_COPT_SS_BASE
+# define SQL_COPT_SS_BASE 1200
+#endif
+
+#ifndef SQL_COPT_SS_MARS_ENABLED
+# define SQL_COPT_SS_MARS_ENABLED (SQL_COPT_SS_BASE + 24)
+#endif
+
+#ifndef SQL_MARS_ENABLED_NO
+# define SQL_MARS_ENABLED_NO 0L
+# define SQL_MARS_ENABLED_YES 1L
+#endif
+
+// unixODBC doesn't define SQL_PARAM_DATA_AVAILABLE even though it
+// claims ODBC version 3.80.
+//
+#if ODBCVER >= 0x0380
+# ifndef SQL_PARAM_DATA_AVAILABLE
+# define SQL_PARAM_DATA_AVAILABLE 101
+# endif
+#endif
#include <odb/post.hxx>
diff --git a/odb/mssql/statement.cxx b/odb/mssql/statement.cxx
new file mode 100644
index 0000000..8e61d82
--- /dev/null
+++ b/odb/mssql/statement.cxx
@@ -0,0 +1,805 @@
+// file : odb/mssql/statement.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2005-2011 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::strlen
+#include <cassert>
+
+#include <odb/tracer.hxx>
+
+#include <odb/mssql/mssql.hxx>
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/exceptions.hxx>
+#include <odb/mssql/error.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mssql
+ {
+ // Mapping of bind::buffer_type to SQL_* SQL types.
+ //
+ 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_REAL, // bind::float4
+ SQL_DOUBLE, // bind::float8
+
+ SQL_VARCHAR, // bind::string
+ SQL_VARCHAR, // bind::long_string
+
+ SQL_WVARCHAR, // bind::nstring
+ SQL_WVARCHAR, // bind::long_nstring
+
+ SQL_VARBINARY, // bind::binary
+ SQL_VARBINARY // bind::long_binary
+ };
+
+ // 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_FLOAT, // bind::float4
+ SQL_C_DOUBLE, // bind::float8
+
+ SQL_C_CHAR, // bind::string
+ SQL_C_CHAR, // bind::long_string
+
+ SQL_C_WCHAR, // bind::nstring
+ SQL_C_WCHAR, // bind::long_nstring
+
+ SQL_C_BINARY, // bind::binary
+ SQL_C_BINARY // bind::long_binary
+ };
+
+ //
+ // statement
+ //
+
+ statement::
+ statement (connection& conn, const string& text)
+ : conn_ (conn), text_copy_ (text), text_ (text_copy_.c_str ())
+ {
+ init (text_copy_.size ());
+ }
+
+ statement::
+ statement (connection& conn, const char* text, bool copy)
+ : conn_ (conn)
+ {
+ size_t n;
+
+ if (copy)
+ {
+ text_copy_ = text;
+ text_ = text_copy_.c_str ();
+ n = text_copy_.size ();
+ }
+ else
+ {
+ text_ = text;
+ n = strlen (text_);
+ }
+
+ init (n);
+ }
+
+ void statement::
+ init (size_t text_size)
+ {
+ SQLRETURN r;
+
+ // Allocate the handle.
+ //
+ {
+ SQLHANDLE h;
+ r = SQLAllocHandle (SQL_HANDLE_STMT, conn_.handle (), &h);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_);
+
+ stmt_.reset (h);
+ }
+
+ // Disable escape sequences.
+ //
+ r = SQLSetStmtAttr (stmt_,
+ SQL_ATTR_NOSCAN,
+ (SQLPOINTER) SQL_NOSCAN_OFF,
+ 0);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ // Prepare the statement.
+ //
+ r = SQLPrepare (stmt_, (SQLCHAR*) text_, (SQLINTEGER) text_size);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->prepare (conn_, *this);
+ }
+ }
+
+ statement::
+ ~statement ()
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->deallocate (conn_, *this);
+ }
+ }
+
+ const char* statement::
+ text () const
+ {
+ return text_;
+ }
+
+ void statement::
+ bind_param (bind* b, size_t n)
+ {
+ SQLRETURN r;
+
+ n++; // Parameters are counted from 1.
+
+ for (size_t i (1); i < n; ++i, ++b)
+ {
+ SQLULEN col_size;
+ SQLPOINTER buf;
+
+ switch (b->type)
+ {
+ case bind::long_string:
+ case bind::long_binary:
+ {
+ buf = (SQLPOINTER) b;
+ col_size = b->capacity != 0
+ ? (SQLULEN) b->capacity
+ : SQL_SS_LENGTH_UNLIMITED;
+ break;
+ }
+ case bind::long_nstring:
+ {
+ buf = (SQLPOINTER) b;
+ col_size = b->capacity != 0
+ ? (SQLULEN) b->capacity / 2 // In characters, not bytes.
+ : SQL_SS_LENGTH_UNLIMITED;
+ break;
+ }
+ case bind::string:
+ case bind::binary:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = (SQLULEN) b->capacity;
+ break;
+ }
+ case bind::nstring:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = (SQLULEN) b->capacity / 2; // In characters, not bytes.
+ break;
+ }
+ default:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ break;
+ }
+ }
+
+ r = SQLBindParameter (
+ stmt_,
+ (SQLUSMALLINT) i,
+ SQL_PARAM_INPUT,
+ c_type_lookup[b->type],
+ sql_type_lookup[b->type],
+ col_size,
+ 0, // decimal digits
+ buf,
+ 0, // buffer capacity (shouldn't be needed for input parameters)
+ b->size_ind);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+ }
+
+ size_t statement::
+ bind_result (bind* b, size_t n)
+ {
+ SQLRETURN r;
+
+ size_t i (0);
+ for (bind* end (b + n); b != end; ++b)
+ {
+ switch (b->type)
+ {
+ case bind::long_string:
+ case bind::long_nstring:
+ case bind::long_binary:
+ {
+ // Long data is not bound.
+ //
+ continue;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ r = SQLBindCol (stmt_,
+ (SQLUSMALLINT) (i + 1), // Results are counted from 1.
+ c_type_lookup[b->type],
+ (SQLPOINTER) b->buffer,
+ b->capacity,
+ b->size_ind);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ ++i;
+ }
+
+ return i;
+ }
+
+ SQLRETURN statement::
+ execute ()
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ SQLRETURN r (SQLExecute (stmt_));
+
+ if (r == SQL_NEED_DATA)
+ {
+ details::buffer& tmp_buf (conn_.long_buffer ());
+
+ if (tmp_buf.capacity () == 0)
+ tmp_buf.capacity (4096);
+
+ bind* b;
+ for (;;)
+ {
+ r = SQLParamData (stmt_, (SQLPOINTER*) &b);
+
+ // If we get anything other than SQL_NEED_DATA, then this is
+ // the return code of SQLExecute().
+ //
+ if (r != SQL_NEED_DATA)
+ break;
+
+ // See if we have a NULL value.
+ //
+ if (*b->size_ind == SQL_NULL_DATA)
+ {
+ r = SQLPutData (stmt_, 0, 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;
+ }
+ }
+ }
+ }
+
+ return r;
+ }
+
+ void statement::
+ stream_result (bind* b, size_t i, size_t n)
+ {
+ details::buffer& tmp_buf (conn_.long_buffer ());
+
+ if (tmp_buf.capacity () == 0)
+ tmp_buf.capacity (4096);
+
+ SQLRETURN r;
+
+ for (bind* end (b + n); b != end; ++b)
+ {
+ bool char_data;
+
+ switch (b->type)
+ {
+ case bind::long_string:
+ case bind::long_nstring:
+ {
+ char_data = true;
+ break;
+ }
+ case bind::long_binary:
+ {
+ char_data = false;
+ break;
+ }
+ default:
+ {
+ continue; // Not long data.
+ }
+ }
+
+ long_callback& cb (*static_cast<long_callback*> (b->buffer));
+
+ // First determine if the value is NULL as well as try to
+ // get the total data size.
+ //
+ SQLLEN si;
+ r = SQLGetData (stmt_,
+ (SQLUSMALLINT) (i + 1),
+ c_type_lookup[b->type],
+ tmp_buf.data (), // Dummy value.
+ 0,
+ &si);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ void* buf;
+ 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));
+
+ for (;;)
+ {
+ cb.callback.result (
+ cb.context.result,
+ &position,
+ &buf,
+ &size,
+ c,
+ size_left,
+ tmp_buf.data (),
+ tmp_buf.capacity ());
+
+ if (c == last_chunk || c == one_chunk || c == null_chunk)
+ break;
+
+ // SQLGetData() can keep returning SQL_SUCCESS_WITH_INFO (truncated)
+ // with SQL_NO_TOTAL for all the calls except the last one. For the
+ // last call we should get SQL_SUCCESS and the size_indicator should
+ // contain a valid value.
+ //
+ r = SQLGetData (stmt_,
+ (SQLUSMALLINT) (i + 1),
+ c_type_lookup[b->type],
+ (SQLPOINTER) buf,
+ (SQLLEN) size,
+ &si);
+
+ if (r == SQL_SUCCESS)
+ {
+ assert (si != SQL_NO_TOTAL);
+
+ // Actual amount of data copied to the buffer (appears not to
+ // include the NULL teminator).
+ //
+ size = static_cast<size_t> (si);
+ c = last_chunk;
+ }
+ else if (r == SQL_SUCCESS_WITH_INFO)
+ {
+ if (char_data)
+ size--; // NULL terminator.
+
+ c = next_chunk;
+ }
+ else
+ translate_error (r, conn_, stmt_);
+
+ // Update the total.
+ //
+ if (size_left != 0)
+ size_left -= size;
+ }
+
+ ++i;
+ }
+ }
+
+ //
+ // select_statement
+ //
+
+ select_statement::
+ select_statement (connection& conn,
+ const string& t,
+ binding& param,
+ binding& result)
+ : statement (conn, t), result_ (result), executed_ (false)
+ {
+ bind_param (param.bind, param.count);
+ first_long_ = bind_result (result.bind, result.count);
+ }
+
+ select_statement::
+ select_statement (connection& conn,
+ const char* t,
+ binding& param,
+ binding& result,
+ bool ct)
+ : statement (conn, t, ct), result_ (result), executed_ (false)
+ {
+ bind_param (param.bind, param.count);
+ first_long_ = bind_result (result.bind, result.count);
+ }
+
+ select_statement::
+ select_statement (connection& conn, const string& t, binding& result)
+ : statement (conn, t), result_ (result), executed_ (false)
+ {
+ first_long_ = bind_result (result.bind, result.count);
+ }
+
+ select_statement::
+ select_statement (connection& conn,
+ const char* t,
+ binding& result,
+ bool ct)
+ : statement (conn, t, ct), result_ (result), executed_ (false)
+ {
+ first_long_ = bind_result (result.bind, result.count);
+ }
+
+ select_statement::
+ ~select_statement ()
+ {
+ if (executed_)
+ {
+ try
+ {
+ free_result ();
+ }
+ catch (...)
+ {
+ }
+ }
+ }
+
+ void select_statement::
+ execute ()
+ {
+ if (executed_)
+ free_result ();
+
+ SQLRETURN r (statement::execute ());
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ executed_ = true;
+ }
+
+ select_statement::result select_statement::
+ fetch ()
+ {
+ SQLRETURN r (SQLFetch (stmt_));
+
+ if (r == SQL_NO_DATA)
+ return no_data;
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ return success;
+ }
+
+ void select_statement::
+ free_result ()
+ {
+ if (executed_)
+ {
+ // If we cannot close the cursor, there is no point in trying again.
+ //
+ executed_ = false;
+
+ SQLRETURN r (SQLCloseCursor (stmt_));
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+ }
+
+ //
+ // insert_statement
+ //
+
+ insert_statement::
+ ~insert_statement ()
+ {
+ }
+
+ insert_statement::
+ insert_statement (connection& conn,
+ const string& t,
+ binding& param,
+ bool returning)
+ : statement (conn, t), returning_ (returning)
+ {
+ bind_param (param.bind, param.count);
+
+ if (returning)
+ init_result ();
+ }
+
+ insert_statement::
+ insert_statement (connection& conn,
+ const char* t,
+ binding& param,
+ bool returning,
+ bool ct)
+ : statement (conn, t, ct), returning_ (returning)
+ {
+ bind_param (param.bind, param.count);
+
+ if (returning)
+ init_result ();
+ }
+
+ void insert_statement::
+ init_result ()
+ {
+ SQLRETURN r (
+ SQLBindCol (stmt_,
+ 1,
+ SQL_C_SBIGINT,
+ (SQLPOINTER) &id_,
+ sizeof (id_),
+ &id_size_ind_));
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+
+ bool insert_statement::
+ execute ()
+ {
+ SQLRETURN r (statement::execute ());
+
+ if (!SQL_SUCCEEDED (r))
+ {
+ // Translate the integrity contraint violation (SQLSTATE 23000)
+ // to the flase return value. This code is similar to that found
+ // in translate_error().
+ //
+ char sqlstate[SQL_SQLSTATE_SIZE + 1];
+ SQLINTEGER native_code;
+ SQLSMALLINT msg_size;
+
+ bool cv (false);
+
+ for (SQLSMALLINT i (1);; ++i)
+ {
+ SQLRETURN r (SQLGetDiagRec (SQL_HANDLE_STMT,
+ stmt_,
+ i,
+ (SQLCHAR*) sqlstate,
+ &native_code,
+ 0,
+ 0,
+ &msg_size));
+
+ if (r == SQL_NO_DATA)
+ break;
+ else if (SQL_SUCCEEDED (r))
+ {
+ string s (sqlstate);
+
+ if (s == "23000") // Integrity contraint violation.
+ cv = true;
+ else if (s != "01000") // General warning.
+ {
+ // Some other code.
+ //
+ cv = false;
+ break;
+ }
+ }
+ else
+ {
+ // SQLGetDiagRec() failure.
+ //
+ cv = false;
+ break;
+ }
+ }
+
+ if (cv)
+ return false;
+ else
+ translate_error (r, conn_, stmt_);
+ }
+
+ // Fetch the row containing the id if this statement is returning.
+ //
+ if (returning_)
+ {
+ r = SQLFetch (stmt_);
+
+ if (r != SQL_NO_DATA && !SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ {
+ SQLRETURN r (SQLCloseCursor (stmt_)); // Don't overwrite r.
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+
+ if (r == SQL_NO_DATA)
+ throw database_exception (
+ 0,
+ "?????",
+ "result set expected from a statement with the OUTPUT clause");
+ }
+
+ return true;
+ }
+
+ //
+ // update_statement
+ //
+
+ update_statement::
+ ~update_statement ()
+ {
+ }
+
+ update_statement::
+ update_statement (connection& conn, const string& t, binding& param)
+ : statement (conn, t)
+ {
+ bind_param (param.bind, param.count);
+ }
+
+ update_statement::
+ update_statement (connection& conn,
+ const char* t,
+ binding& param,
+ bool ct)
+ : statement (conn, t, ct)
+ {
+ bind_param (param.bind, param.count);
+ }
+
+ unsigned long long update_statement::
+ execute ()
+ {
+ SQLRETURN r (statement::execute ());
+
+ // SQL_NO_DATA indicates that the statement hasn't affected any rows.
+ //
+ if (r == SQL_NO_DATA)
+ return 0;
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ // Get the number of affected rows.
+ //
+ SQLLEN rows;
+ r = SQLRowCount (stmt_, &rows);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ return static_cast<unsigned long long> (rows);
+ }
+
+ //
+ // delete_statement
+ //
+
+ delete_statement::
+ ~delete_statement ()
+ {
+ }
+
+ delete_statement::
+ delete_statement (connection& conn, const string& t, binding& param)
+ : statement (conn, t)
+ {
+ bind_param (param.bind, param.count);
+ }
+
+ delete_statement::
+ delete_statement (connection& conn,
+ const char* t,
+ binding& param,
+ bool ct)
+ : statement (conn, t, ct)
+ {
+ bind_param (param.bind, param.count);
+ }
+
+ unsigned long long delete_statement::
+ execute ()
+ {
+ SQLRETURN r (statement::execute ());
+
+ // SQL_NO_DATA indicates that the statement hasn't affected any rows.
+ //
+ if (r == SQL_NO_DATA)
+ return 0;
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ // Get the number of affected rows.
+ //
+ SQLLEN rows;
+ r = SQLRowCount (stmt_, &rows);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ return static_cast<unsigned long long> (rows);
+ }
+ }
+}
diff --git a/odb/mssql/statement.hxx b/odb/mssql/statement.hxx
new file mode 100644
index 0000000..15e64a0
--- /dev/null
+++ b/odb/mssql/statement.hxx
@@ -0,0 +1,228 @@
+// file : odb/mssql/statement.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2005-2011 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_STATEMENT_HXX
+#define ODB_MSSQL_STATEMENT_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/statement.hxx>
+
+#include <odb/mssql/mssql-fwd.hxx>
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/binding.hxx>
+#include <odb/mssql/auto-handle.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class connection;
+
+ class LIBODB_MSSQL_EXPORT statement: public odb::statement
+ {
+ public:
+ virtual
+ ~statement () = 0;
+
+ SQLHSTMT
+ handle () const
+ {
+ return stmt_;
+ }
+
+ virtual const char*
+ text () const;
+
+ protected:
+ statement (connection&, const std::string& text);
+ statement (connection&, const char* text, bool copy_text);
+
+ private:
+ void
+ init (std::size_t text_size);
+
+ protected:
+ void
+ bind_param (bind*, std::size_t count);
+
+ std::size_t
+ bind_result (bind*, std::size_t count);
+
+ SQLRETURN
+ execute ();
+
+ void
+ stream_result (bind*, std::size_t start, std::size_t count);
+
+ protected:
+ connection& conn_;
+ std::string text_copy_;
+ const char* text_;
+ auto_handle<SQL_HANDLE_STMT> stmt_;
+ };
+
+ class LIBODB_MSSQL_EXPORT select_statement: public statement
+ {
+ public:
+ virtual
+ ~select_statement ();
+
+ // While the long data columns can appear in any order in the
+ // result binding, they should appear last in the statement
+ // text.
+ //
+ select_statement (connection& conn,
+ const std::string& text,
+ binding& param,
+ binding& result);
+
+ select_statement (connection& conn,
+ const char* text,
+ binding& param,
+ binding& result,
+ bool copy_text = true);
+
+ select_statement (connection& conn,
+ const std::string& text,
+ binding& result);
+
+ select_statement (connection& conn,
+ const char* text,
+ binding& result,
+ bool copy_text = true);
+
+ enum result
+ {
+ success,
+ no_data
+ };
+
+ void
+ execute ();
+
+ result
+ fetch ();
+
+ void
+ stream_result ()
+ {
+ if (first_long_ != result_.count)
+ statement::stream_result (result_.bind, first_long_, result_.count);
+ }
+
+ void
+ free_result ();
+
+ private:
+ select_statement (const select_statement&);
+ select_statement& operator= (const select_statement&);
+
+ private:
+ binding& result_;
+ std::size_t first_long_; // First long data column.
+ bool executed_;
+ };
+
+ class LIBODB_MSSQL_EXPORT insert_statement: public statement
+ {
+ public:
+ virtual
+ ~insert_statement ();
+
+ insert_statement (connection& conn,
+ const std::string& text,
+ binding& param,
+ bool returning);
+
+ insert_statement (connection& conn,
+ const char* text,
+ binding& param,
+ bool returning,
+ bool copy_text = true);
+
+ // Return true if successful and false if the row is a duplicate.
+ // All other errors are reported by throwing exceptions.
+ //
+ bool
+ execute ();
+
+ unsigned long long
+ id ()
+ {
+ return id_;
+ }
+
+ private:
+ insert_statement (const insert_statement&);
+ insert_statement& operator= (const insert_statement&);
+
+ private:
+ void
+ init_result ();
+
+ private:
+ bool returning_;
+ unsigned long long id_;
+ SQLLEN id_size_ind_;
+ };
+
+ class LIBODB_MSSQL_EXPORT update_statement: public statement
+ {
+ public:
+ virtual
+ ~update_statement ();
+
+ update_statement (connection& conn,
+ const std::string& text,
+ binding& param);
+
+ update_statement (connection& conn,
+ const char* text,
+ binding& param,
+ bool copy_text = true);
+
+ unsigned long long
+ execute ();
+
+ private:
+ update_statement (const update_statement&);
+ update_statement& operator= (const update_statement&);
+ };
+
+ class LIBODB_MSSQL_EXPORT delete_statement: public statement
+ {
+ public:
+ virtual
+ ~delete_statement ();
+
+ delete_statement (connection& conn,
+ const std::string& text,
+ binding& param);
+
+ delete_statement (connection& conn,
+ const char* text,
+ binding& param,
+ bool copy_text = true);
+
+ unsigned long long
+ execute ();
+
+ private:
+ delete_statement (const delete_statement&);
+ delete_statement& operator= (const delete_statement&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_STATEMENT_HXX
diff --git a/odb/mssql/tracer.cxx b/odb/mssql/tracer.cxx
new file mode 100644
index 0000000..023d192
--- /dev/null
+++ b/odb/mssql/tracer.cxx
@@ -0,0 +1,62 @@
+// file : odb/mssql/tracer.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/mssql/tracer.hxx>
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/statement.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ tracer::
+ ~tracer ()
+ {
+ }
+
+ void tracer::
+ prepare (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ execute (connection& c, const statement& s)
+ {
+ execute (c, s.text ());
+ }
+
+ void tracer::
+ deallocate (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ prepare (odb::connection& c, const odb::statement& s)
+ {
+ prepare (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const odb::statement& s)
+ {
+ execute (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const char* s)
+ {
+ execute (static_cast<connection&> (c), s);
+ }
+
+ void tracer::
+ deallocate (odb::connection& c, const odb::statement& s)
+ {
+ deallocate (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+ }
+}
diff --git a/odb/mssql/tracer.hxx b/odb/mssql/tracer.hxx
new file mode 100644
index 0000000..46c7c48
--- /dev/null
+++ b/odb/mssql/tracer.hxx
@@ -0,0 +1,63 @@
+// file : odb/mssql/tracer.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_TRACER_HXX
+#define ODB_MSSQL_TRACER_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/tracer.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class LIBODB_MSSQL_EXPORT tracer: private odb::tracer
+ {
+ public:
+ virtual
+ ~tracer ();
+
+ virtual void
+ prepare (connection&, const statement&);
+
+ virtual void
+ execute (connection&, const statement&);
+
+ virtual void
+ execute (connection&, const char* statement) = 0;
+
+ virtual void
+ deallocate (connection&, const statement&);
+
+ private:
+ // Allow these classes to convert mssql::tracer to odb::tracer.
+ //
+ friend class database;
+ friend class connection;
+ friend class transaction;
+
+ virtual void
+ prepare (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const char* statement);
+
+ virtual void
+ deallocate (odb::connection&, const odb::statement&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_TRACER_HXX
diff --git a/odb/mssql/transaction-impl.cxx b/odb/mssql/transaction-impl.cxx
index 308f078..68fee22 100644
--- a/odb/mssql/transaction-impl.cxx
+++ b/odb/mssql/transaction-impl.cxx
@@ -43,14 +43,11 @@ namespace odb
odb::transaction_impl::connection_ = connection_.get ();
}
- /*
- @@ TODO
{
odb::tracer* t;
if ((t = connection_->tracer ()) || (t = database_.tracer ()))
t->execute (*connection_, "BEGIN");
}
- */
// In ODBC a transaction is started automatically before the first
// statement is executed.
@@ -60,14 +57,11 @@ namespace odb
void transaction_impl::
commit ()
{
- /*
- @@ TODO
{
odb::tracer* t;
if ((t = connection_->tracer ()) || (t = database_.tracer ()))
t->execute (*connection_, "COMMIT");
}
- */
SQLRETURN r (
SQLEndTran (SQL_HANDLE_DBC, connection_->handle (), SQL_COMMIT));
@@ -84,14 +78,11 @@ namespace odb
void transaction_impl::
rollback ()
{
- /*
- @@ TODO
{
odb::tracer* t;
if ((t = connection_->tracer ()) || (t = database_.tracer ()))
t->execute (*connection_, "ROLLBACK");
}
- */
SQLRETURN r (
SQLEndTran (SQL_HANDLE_DBC, connection_->handle (), SQL_ROLLBACK));
diff --git a/odb/mssql/transaction.hxx b/odb/mssql/transaction.hxx
index 57806b8..9837401 100644
--- a/odb/mssql/transaction.hxx
+++ b/odb/mssql/transaction.hxx
@@ -3,8 +3,6 @@
// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
// license : ODB NCUEL; see accompanying LICENSE file
-//@@ disabled functionality
-
#ifndef ODB_MSSQL_TRANSACTION_HXX
#define ODB_MSSQL_TRANSACTION_HXX
@@ -14,7 +12,7 @@
#include <odb/mssql/version.hxx>
#include <odb/mssql/forward.hxx>
-//#include <odb/mssql/tracer.hxx>
+#include <odb/mssql/tracer.hxx>
#include <odb/mssql/details/export.hxx>
@@ -54,7 +52,6 @@ namespace odb
static void
current (transaction&);
- /*
// SQL statement tracing.
//
public:
@@ -73,7 +70,6 @@ namespace odb
}
using odb::transaction::tracer;
- */
public:
transaction_impl&