summaryrefslogtreecommitdiff
path: root/odb/odb/relational/mssql/source.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'odb/odb/relational/mssql/source.cxx')
-rw-r--r--odb/odb/relational/mssql/source.cxx1201
1 files changed, 1201 insertions, 0 deletions
diff --git a/odb/odb/relational/mssql/source.cxx b/odb/odb/relational/mssql/source.cxx
new file mode 100644
index 0000000..573104d
--- /dev/null
+++ b/odb/odb/relational/mssql/source.cxx
@@ -0,0 +1,1201 @@
+// file : odb/relational/mssql/source.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/source.hxx>
+
+#include <odb/relational/mssql/common.hxx>
+#include <odb/relational/mssql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mssql
+ {
+ namespace source
+ {
+ namespace relational = relational::source;
+
+ //
+ //
+ struct query_parameters: relational::query_parameters
+ {
+ query_parameters (base const& x): base (x) {}
+
+ virtual string
+ auto_id (semantics::data_member&, const string&, const string&)
+ {
+ return "";
+ }
+ };
+ entry<query_parameters> query_parameters_;
+
+ //
+ //
+ struct object_columns: relational::object_columns, context
+ {
+ object_columns (base const& x)
+ : base (x), rowversion_ (false), column_count_ (0) {}
+
+ virtual bool
+ column (semantics::data_member& m,
+ string const& table,
+ string const& column)
+ {
+ // Don't add a column for auto id in the INSERT statement.
+ // Only simple, direct id can be auto.
+ //
+ if (sk_ == statement_insert && key_prefix_.empty () && auto_ (m))
+ return false;
+
+ // Don't update the ROWVERSION column explicitly.
+ //
+ if (sk_ == statement_update)
+ {
+ sql_type t (parse_sql_type (column_type (), m));
+ if (t.type == sql_type::ROWVERSION)
+ {
+ rowversion_ = true;
+ return false;
+ }
+ }
+
+ bool r (base::column (m, table, column));
+
+ // Count the number of columns in the UPDATE statement, but
+ // excluding soft-deleted.
+ //
+ if (sk_ == statement_update && r && !deleted (member_path_))
+ column_count_++;
+
+ return r;
+ }
+
+ virtual void
+ traverse_post (semantics::nameable& n)
+ {
+ if (rowversion_ && column_count_ == 0)
+ {
+ location l (n.location ());
+ error (l) << "ROWVERSION in an object without any readwrite "
+ "data members" << endl;
+ error (l) << "UPDATE statement will be empty" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ private:
+ bool rowversion_;
+ size_t column_count_;
+ };
+ entry<object_columns> object_columns_;
+
+ //
+ //
+ struct persist_statement_params: relational::persist_statement_params,
+ context
+ {
+ persist_statement_params (base const& x): base (x) {}
+
+ virtual string
+ version_value (semantics::data_member& m)
+ {
+ sql_type t (parse_sql_type (column_type (), m));
+ return t.type == sql_type::ROWVERSION ? "DEFAULT" : "1";
+ }
+ };
+ entry<persist_statement_params> persist_statement_params_;
+
+ //
+ // bind
+ //
+
+ static const char* integer_buffer_types[] =
+ {
+ "mssql::bind::bit",
+ "mssql::bind::tinyint",
+ "mssql::bind::smallint",
+ "mssql::bind::int_",
+ "mssql::bind::bigint"
+ };
+
+ struct bind_member: relational::bind_member_impl<sql_type>,
+ member_base
+ {
+ bind_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << b << ".type = " <<
+ integer_buffer_types[mi.st->type - sql_type::BIT] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;";
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::decimal;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ // Encode precision (p) and scale (s) as (p * 100 + s).
+ //
+ << b << ".capacity = " << mi.st->prec * 100 + mi.st->scale << ";";
+ }
+
+ virtual void
+ traverse_smallmoney (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::smallmoney;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;";
+ }
+
+ virtual void
+ traverse_money (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::money;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;";
+ }
+
+ virtual void
+ traverse_float4 (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::float4;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ << b << ".capacity = " << mi.st->prec << ";";
+ }
+
+ virtual void
+ traverse_float8 (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::float8;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ << b << ".capacity = " << mi.st->prec << ";";
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::string;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ << b << ".capacity = static_cast<SQLLEN> (sizeof (" <<
+ arg << "." << mi.var << "value));";
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::long_string;"
+ << b << ".buffer = &" << arg << "." << mi.var << "callback;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ // Encode the column size with 0 indicating unlimited.
+ //
+ << b << ".capacity = " << mi.st->prec << ";";
+ }
+
+ virtual void
+ traverse_nstring (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::nstring;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ << b << ".capacity = static_cast<SQLLEN> (sizeof (" <<
+ arg << "." << mi.var << "value));";
+ }
+
+ virtual void
+ traverse_long_nstring (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::long_nstring;"
+ << b << ".buffer = &" << arg << "." << mi.var << "callback;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ // Encode the column size (in bytes) with 0 indicating unlimited.
+ //
+ << b << ".capacity = " << mi.st->prec * 2 << ";";
+ }
+
+ virtual void
+ traverse_binary (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::binary;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ << b << ".capacity = static_cast<SQLLEN> (sizeof (" <<
+ arg << "." << mi.var << "value));";
+ }
+
+ virtual void
+ traverse_long_binary (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::long_binary;"
+ << b << ".buffer = &" << arg << "." << mi.var << "callback;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ // Encode the column size with 0 indicating unlimited.
+ //
+ << b << ".capacity = " << mi.st->prec << ";";
+ }
+
+ virtual void
+ traverse_date (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::date;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;";
+ }
+
+ virtual void
+ traverse_time (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::time;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ // Encode fractional seconds (scale).
+ //
+ << b << ".capacity = " << mi.st->scale << ";";
+ }
+
+ virtual void
+ traverse_datetime (member_info& mi)
+ {
+ unsigned short scale (0);
+
+ switch (mi.st->type)
+ {
+ case sql_type::DATETIME:
+ {
+ // Looks like it is 3 (rounded to 0.000, 0.003, or 0.007).
+ //
+ scale = 3;
+ break;
+ }
+ case sql_type::DATETIME2:
+ {
+ scale = mi.st->scale;
+ break;
+ }
+ case sql_type::SMALLDATETIME:
+ {
+ // No seconds in SMALLDATATIME. Encode it a special precision
+ // value (8).
+ //
+ scale = 8;
+ break;
+ }
+ default:
+ {
+ assert (false);
+ break;
+ }
+ }
+
+ os << b << ".type = mssql::bind::datetime;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ // Encode fractional seconds (scale).
+ //
+ << b << ".capacity = " << scale << ";";
+ }
+
+ virtual void
+ traverse_datetimeoffset (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::datetimeoffset;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ // Encode fractional seconds (scale).
+ //
+ << b << ".capacity = " << mi.st->scale << ";";
+ }
+
+ virtual void
+ traverse_uniqueidentifier (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::uniqueidentifier;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;";
+ }
+
+ virtual void
+ traverse_rowversion (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::rowversion;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;";
+ }
+ };
+ entry<bind_member> bind_member_;
+
+ //
+ // init image
+ //
+
+ struct init_image_member: relational::init_image_member_impl<sql_type>,
+ member_base
+ {
+ init_image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ set_null (member_info& mi)
+ {
+ os << "i." << mi.var << "size_ind = SQL_NULL_DATA;";
+ }
+
+ virtual void
+ check_accessor (member_info& mi, member_access& ma)
+ {
+ // We cannot use accessors that return by-value for long data
+ // members.
+ //
+ if (long_data (*mi.st) && ma.by_value)
+ {
+ error (ma.loc) << "accessor returning a value cannot be used "
+ << "for a data member of SQL Server long data "
+ << "type" << endl;
+ info (ma.loc) << "accessor returning a const reference is required"
+ << endl;
+ info (mi.m.location ()) << "data member is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 0;";
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 0;";
+ }
+
+ virtual void
+ traverse_smallmoney (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 4;";
+ }
+
+ virtual void
+ traverse_money (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 8;";
+ }
+
+ virtual void
+ traverse_float4 (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 0;";
+ }
+
+ virtual void
+ traverse_float8 (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 0;";
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ // Don't mention the extra character for the null-terminator.
+ << "sizeof (i." << mi.var << "value) - 1," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size_ind =" << endl
+ << " is_null ? SQL_NULL_DATA : static_cast<SQLLEN> (size);";
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "callback.callback.param," << endl
+ << "i." << mi.var << "callback.context.param," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? " <<
+ "SQL_NULL_DATA : SQL_DATA_AT_EXEC;";
+ }
+
+ virtual void
+ traverse_nstring (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ // Don't mention the extra character for the null-terminator.
+ << "sizeof (i." << mi.var << "value) / 2 - 1," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size_ind =" << endl
+ << " is_null ? SQL_NULL_DATA : static_cast<SQLLEN> (size * 2);";
+ }
+
+ virtual void
+ traverse_long_nstring (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "callback.callback.param," << endl
+ << "i." << mi.var << "callback.context.param," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? " <<
+ "SQL_NULL_DATA : SQL_DATA_AT_EXEC;";
+ }
+
+ virtual void
+ traverse_binary (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "sizeof (i." << mi.var << "value)," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size_ind =" << endl
+ << " is_null ? SQL_NULL_DATA : static_cast<SQLLEN> (size);";
+ }
+
+ virtual void
+ traverse_long_binary (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "callback.callback.param," << endl
+ << "i." << mi.var << "callback.context.param," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? " <<
+ "SQL_NULL_DATA : SQL_DATA_AT_EXEC;";
+ }
+
+ virtual void
+ traverse_date (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 0;";
+ }
+
+ virtual void
+ traverse_time (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, " << mi.st->scale << ", " <<
+ "is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null" << endl
+ << " ? SQL_NULL_DATA" << endl
+ << " : static_cast<SQLLEN> (sizeof (i." << mi.var << "value));";
+ }
+
+ virtual void
+ traverse_datetime (member_info& mi)
+ {
+ // The same code as in bind.
+ //
+ unsigned short scale (0);
+
+ switch (mi.st->type)
+ {
+ case sql_type::DATETIME:
+ {
+ scale = 3;
+ break;
+ }
+ case sql_type::DATETIME2:
+ {
+ scale = mi.st->scale;
+ break;
+ }
+ case sql_type::SMALLDATETIME:
+ {
+ scale = 8;
+ break;
+ }
+ default:
+ {
+ assert (false);
+ break;
+ }
+ }
+
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, " << scale << ", " <<
+ "is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 0;";
+ }
+
+ virtual void
+ traverse_datetimeoffset (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, " << mi.st->scale << ", " <<
+ "is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null" << endl
+ << " ? SQL_NULL_DATA" << endl
+ << " : static_cast<SQLLEN> (sizeof (i." << mi.var << "value));";
+ }
+
+ virtual void
+ traverse_uniqueidentifier (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 0;";
+ }
+
+ virtual void
+ traverse_rowversion (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 8;";
+ }
+ };
+ entry<init_image_member> init_image_member_;
+
+ //
+ // init value
+ //
+
+ struct init_value_member: relational::init_value_member_impl<sql_type>,
+ member_base
+ {
+ init_value_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ get_null (string const& var) const
+ {
+ os << "i." << var << "size_ind == SQL_NULL_DATA";
+ }
+
+ virtual void
+ check_modifier (member_info& mi, member_access& ma)
+ {
+ // We cannot use by-value modifier for long data members.
+ //
+ if (long_data (*mi.st) && ma.placeholder ())
+ {
+ error (ma.loc) << "modifier accepting a value cannot be used "
+ << "for a data member of SQL Server long data "
+ << "type" << endl;
+ info (ma.loc) << "modifier returning a non-const reference is "
+ << "required" << endl;
+ info (mi.m.location ()) << "data member is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_smallmoney (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_money (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_float4 (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_float8 (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "static_cast<std::size_t> (i." << mi.var << "size_ind)," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "callback.callback.result," << endl
+ << "i." << mi.var << "callback.context.result);"
+ << endl;
+ }
+
+ virtual void
+ traverse_nstring (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "static_cast<std::size_t> (" <<
+ "i." << mi.var << "size_ind / 2)," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_long_nstring (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "callback.callback.result," << endl
+ << "i." << mi.var << "callback.context.result);"
+ << endl;
+ }
+
+ virtual void
+ traverse_binary (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "static_cast<std::size_t> (i." << mi.var << "size_ind)," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_long_binary (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "callback.callback.result," << endl
+ << "i." << mi.var << "callback.context.result);"
+ << endl;
+ }
+
+ virtual void
+ traverse_date (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_time (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_datetime (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_datetimeoffset (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_uniqueidentifier (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_rowversion (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+ };
+ entry<init_value_member> init_value_member_;
+
+ struct statement_columns_common: context
+ {
+ void
+ process (relational::statement_columns& cols,
+ statement_kind sk,
+ bool dynamic)
+ {
+ using relational::statement_columns;
+
+ // Long data columns must come last in the SELECT statement. If
+ // this statement is going to be processed at runtime, then this
+ // will be taken care of then.
+ //
+ if (sk != statement_select || dynamic)
+ return;
+
+ // Go over the columns list while keeping track of how many
+ // columns we have examined. If the current column is long data,
+ // then move it to the back. Stop once we have examined all the
+ // columns.
+ //
+ size_t n (cols.size ());
+ for (statement_columns::iterator i (cols.begin ()); n != 0; --n)
+ {
+ if (long_data (parse_sql_type (i->type, *i->member)))
+ {
+ cols.push_back (*i);
+ i = cols.erase (i);
+ }
+ else
+ ++i;
+ }
+ }
+ };
+
+ struct container_traits: relational::container_traits,
+ statement_columns_common
+ {
+ container_traits (base const& x): base (x) {}
+
+ virtual void
+ cache_result (string const&)
+ {
+ // Caching is not necessary since with MARS enabled SQL Server
+ // can execute several interleaving statements.
+ //
+ }
+
+ virtual void
+ init_value_extra ()
+ {
+ os << "sts.select_statement ().stream_result ();"
+ << endl;
+ }
+
+ virtual void
+ process_statement_columns (relational::statement_columns& cols,
+ statement_kind sk,
+ bool dynamic)
+ {
+ statement_columns_common::process (cols, sk, dynamic);
+ }
+ };
+ entry<container_traits> container_traits_;
+
+ struct section_traits: relational::section_traits,
+ statement_columns_common
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ init_value_extra ()
+ {
+ os << "st.stream_result ();";
+ }
+
+ virtual void
+ process_statement_columns (relational::statement_columns& cols,
+ statement_kind sk,
+ bool dynamic)
+ {
+ statement_columns_common::process (cols, sk, dynamic);
+ }
+
+ virtual string
+ optimistic_version_increment (semantics::data_member& m)
+ {
+ sql_type t (parse_sql_type (column_type (m), m));
+ return t.type != sql_type::ROWVERSION
+ ? "1"
+ : "version (sts.id_image ())";
+ }
+
+ virtual string
+ update_statement_extra (user_section&)
+ {
+ string r;
+
+ semantics::data_member* ver (optimistic (c_));
+
+ if (ver == 0 ||
+ parse_sql_type (column_type (*ver), *ver).type !=
+ sql_type::ROWVERSION)
+ return r;
+
+ // ROWVERSION & SQL Server 2005 incompatibility is detected
+ // in persist_statement_extra.
+ //
+ r = "OUTPUT INSERTED." +
+ convert_from (column_qname (*ver, column_prefix ()), *ver);
+
+ return r;
+ }
+ };
+ entry<section_traits> section_traits_;
+
+ struct class_: relational::class_, statement_columns_common
+ {
+ class_ (base const& x):
+ base (x), init_version_value_member_id_image_ ("v", "version_") {}
+
+ virtual void
+ init_image_pre (type& c)
+ {
+ if (options.generate_query () &&
+ !(composite (c) || (abstract (c) && !polymorphic (c))))
+ {
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ if (poly_derived)
+ os << "{"
+ << "root_traits::image_type& ri (root_image (i));"
+ << endl;
+
+ string i (poly_derived ? "ri" : "i");
+
+ os << "if (" << i << ".change_callback_.callback != 0)" << endl
+ << "(" << i << ".change_callback_.callback) (" <<
+ i << ".change_callback_.context);";
+
+ if (poly_derived)
+ os << "}";
+ else
+ os << endl;
+ }
+ }
+
+ virtual void
+ init_value_extra ()
+ {
+ os << "st.stream_result ();";
+ }
+
+ virtual string
+ persist_statement_extra (type& c,
+ relational::query_parameters&,
+ persist_position p)
+ {
+ string r;
+
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ // If we are a derived type in a polymorphic hierarchy, then
+ // auto id/version are handled by the root.
+ //
+ if (poly_derived)
+ return r;
+
+ // See if we have auto id or ROWVERSION version.
+ //
+ data_member_path* id (id_member (c));
+ semantics::data_member* ver (optimistic (c));
+
+ if (id != 0 && !auto_ (*id))
+ id = 0;
+
+ if (ver != 0)
+ {
+ sql_type t (parse_sql_type (column_type (*ver), *ver));
+ if (t.type != sql_type::ROWVERSION)
+ ver = 0;
+ }
+
+ if (id == 0 && ver == 0)
+ return r;
+
+ // SQL Server 2005 has a bug that causes it to fail on an
+ // INSERT statement with the OUTPUT clause if data for one
+ // of the inserted columns is supplied at execution (long
+ // data). To work around this problem we use the less
+ // efficient batch of INSERT and SELECT statements.
+ //
+ if (options.mssql_server_version () <= mssql_version (9, 0))
+ {
+ bool ld (false);
+
+ if (c.count ("mssql-has-long-data"))
+ ld = c.get<bool> ("mssql-has-long-data");
+ else
+ {
+ has_long_data t (ld);
+ t.traverse (c);
+ c.set ("mssql-has-long-data", ld);
+ }
+
+ if (ld)
+ {
+ if (p == persist_after_values)
+ {
+ // SQL Server 2005 has no eqivalent of SCOPE_IDENTITY for
+ // ROWVERSION.
+ //
+ if (ver != 0)
+ {
+ error (c.location ()) << "in SQL Server 2005 ROWVERSION " <<
+ "value cannot be retrieved for a persistent class " <<
+ "containing long data" << endl;
+ throw operation_failed ();
+ }
+
+ // We also cannot support bulk INSERT.
+ //
+ if (c.count ("bulk-persist"))
+ {
+ error (c.location ()) << "in SQL Server 2005 bulk " <<
+ "persist operation cannot be implemented for a " <<
+ "persistent class containing long data" << endl;
+ throw operation_failed ();
+ }
+
+ r = "; SELECT " +
+ convert_from ("SCOPE_IDENTITY()", *id->back ());
+ }
+
+ return r;
+ }
+ }
+
+ if (p == persist_after_columns)
+ {
+ r = "OUTPUT ";
+
+ // Top-level auto id column.
+ //
+ if (id != 0)
+ r += "INSERTED." +
+ convert_from (column_qname (*id), *id->back ());
+
+ // Top-level version column.
+ //
+ if (ver != 0)
+ {
+ if (id != 0)
+ r += ',';
+
+ r += "INSERTED." + convert_from (
+ column_qname (*ver, column_prefix ()), *ver);
+ }
+ }
+
+ return r;
+ }
+
+ virtual string
+ update_statement_extra (type& c)
+ {
+ string r;
+
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ // If we are a derived type in a polymorphic hierarchy, then
+ // version is handled by the root.
+ //
+ if (poly_derived)
+ return r;
+
+ semantics::data_member* ver (optimistic (c));
+
+ if (ver == 0 ||
+ parse_sql_type (column_type (*ver), *ver).type !=
+ sql_type::ROWVERSION)
+ return r;
+
+ // Long data & SQL Server 2005 incompatibility is detected
+ // in persist_statement_extra.
+ //
+ r = "OUTPUT INSERTED." +
+ convert_from (column_qname (*ver, column_prefix ()), *ver);
+
+ return r;
+ }
+
+ virtual void
+ process_statement_columns (relational::statement_columns& cols,
+ statement_kind sk,
+ bool dynamic)
+ {
+ statement_columns_common::process (cols, sk, dynamic);
+ }
+
+ virtual string
+ optimistic_version_init (semantics::data_member& m, bool index)
+ {
+ sql_type t (parse_sql_type (column_type (m), m));
+ return t.type != sql_type::ROWVERSION
+ ? "1"
+ : (index
+ ? "version (sts.id_image (i))"
+ : "version (sts.id_image ())");
+ }
+
+ virtual string
+ optimistic_version_increment (semantics::data_member& m, bool index)
+ {
+ sql_type t (parse_sql_type (column_type (m), m));
+ return t.type != sql_type::ROWVERSION
+ ? "1"
+ : (index
+ ? "version (sts.id_image (i))"
+ : "version (sts.id_image ())");
+ }
+
+ virtual bool
+ optimistic_insert_bind_version (semantics::data_member& m)
+ {
+ sql_type t (parse_sql_type (column_type (m), m));
+ return t.type == sql_type::ROWVERSION;
+ }
+
+ virtual void
+ object_extra (type& c)
+ {
+ bool abst (abstract (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ if (poly_derived || (abst && !poly))
+ return;
+
+ if (semantics::data_member* m = optimistic (c))
+ {
+ sql_type t (parse_sql_type (column_type (*m), *m));
+ if (t.type == sql_type::ROWVERSION)
+ {
+ string const& type (class_fq_name (c));
+ string traits ("access::object_traits_impl< " + type + ", id_" +
+ db.string () + " >");
+
+ os << traits << "::version_type" << endl
+ << traits << "::" << endl
+ << "version (const id_image_type& i)"
+ << "{"
+ << "version_type v;";
+ init_version_value_member_id_image_->traverse (*m);
+ os << "return v;"
+ << "}";
+ }
+ }
+ }
+
+ virtual string
+ from_trailer (type& c)
+ {
+ return c.get<view_query> ("query").for_update
+ ? " WITH (UPDLOCK)"
+ : "";
+ }
+
+ virtual string
+ select_trailer (type&) {return "";}
+
+ private:
+ // Go via the dynamic creation to get access to the constructor.
+ //
+ instance<relational::init_value_member>
+ init_version_value_member_id_image_;
+ };
+ entry<class_> class_entry_;
+ }
+ }
+}