// file : odb/relational/mssql/source.cxx // author : Constantin Michael // copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file #include #include #include #include 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 () { return ""; } }; entry query_parameters_; // // struct object_columns: relational::object_columns, context { object_columns (base const& x): base (x) {} virtual void column (semantics::data_member& m, string const& key_prefix, string const& table, string const& column) { // Don't add a column for auto id in the INSERT statement. // if (!(sk_ == statement_insert && key_prefix.empty () && id (m) && auto_(m))) { base::column (m, key_prefix, table, column); } } }; entry object_columns_; // // 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, member_base { bind_member (base const& x) : member_base::base (x), // virtual base base (x), member_base (x) { } virtual bool pre (member_info& mi) { if (container (mi)) return false; ostringstream ostr; ostr << "b[n]"; b = ostr.str (); arg = arg_override_.empty () ? string ("i") : arg_override_; if (var_override_.empty ()) { os << "// " << mi.m.name () << endl << "//" << endl; if (id (mi.m) && auto_ (mi.m)) // For SQL Server we don't send auto id in INSERT. // os << "if (sk != statement_insert && sk != statement_update)" << "{"; else if (inverse (mi.m, key_prefix_) || version (mi.m)) os << "if (sk == statement_select)" << "{"; // If the whole class is readonly, then we will never be // called with sk == statement_update. // else if (!readonly (*context::top_object)) { semantics::class_* c; if (id (mi.m) || readonly (mi.m) || ((c = composite (mi.t)) && readonly (*c))) os << "if (sk != statement_update)" << "{"; } } return true; } virtual void post (member_info& mi) { if (var_override_.empty ()) { semantics::class_* c; if ((c = composite (mi.t))) { bool ro (readonly (*c)); column_count_type const& cc (column_count (*c)); os << "n += " << cc.total << "UL"; // select = total // insert = total - inverse // update = total - inverse - readonly // if (cc.inverse != 0 || (!ro && cc.readonly != 0)) { os << " - (" << endl << "sk == statement_select ? 0 : "; if (cc.inverse != 0) os << cc.inverse << "UL"; if (!ro && cc.readonly != 0) { if (cc.inverse != 0) os << " + "; os << "(" << endl << "sk == statement_insert ? 0 : " << cc.readonly << "UL)"; } os << ")"; } os << ";"; } else os << "n++;"; bool block (false); // The same logic as in pre(). // if (id (mi.m) && auto_ (mi.m)) block = true; else if (inverse (mi.m, key_prefix_) || version (mi.m)) block = true; else if (!readonly (*context::top_object)) { semantics::class_* c; if (id (mi.m) || readonly (mi.m) || ((c = composite (mi.t)) && readonly (*c))) block = true; } if (block) os << "}"; else os << endl; } } virtual void traverse_composite (member_info& mi) { os << "composite_value_traits< " << mi.fq_type () << " >::bind (b + n, " << arg << "." << mi.var << "value, sk);"; } 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 (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 (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 (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;"; } private: string b; string arg; }; entry bind_member_; // // init image // struct init_image_member: relational::init_image_member, member_base { init_image_member (base const& x) : member_base::base (x), // virtual base base (x), member_base (x), member_database_type_id_ (base::type_override_, base::fq_type_override_, base::key_prefix_) { } virtual bool pre (member_info& mi) { // Ignore containers (they get their own table) and inverse // object pointers (they are not present in this binding). // if (container (mi) || inverse (mi.m, key_prefix_)) return false; if (!member_override_.empty ()) member = member_override_; else { // If we are generating standard init() and this member // contains version, ignore it. // if (version (mi.m)) return false; // For SQL Server we don't send auto id in INSERT statement // (nor in UPDATE, as for other databases). So ignore it // altogether. // if (id (mi.m) && auto_ (mi.m)) return false; string const& name (mi.m.name ()); member = "o." + name; os << "// " << name << endl << "//" << endl; // If the whole class is readonly, then we will never be // called with sk == statement_update. // if (!readonly (*context::top_object)) { semantics::class_* c; if (id (mi.m) || readonly (mi.m) || ((c = composite (mi.t)) && readonly (*c))) os << "if (sk == statement_insert)"; } } // If this is a wrapped composite value, then we need to // "unwrap" it. For simple values this is taken care of // by the value_traits specializations. // if (mi.wrapper != 0 && composite (mi.t)) { // Here we need the wrapper type, not the wrapped type. // member = "wrapper_traits< " + mi.fq_type (false) + " >::" + "get_ref (" + member + ")"; } if (composite (mi.t)) { os << "{"; traits = "composite_value_traits< " + mi.fq_type () + " >"; } else { // When handling a pointer, mi.t is the id type of the referenced // object. // semantics::type& mt (member_utype (mi.m, key_prefix_)); if (semantics::class_* c = object_pointer (mt)) { type = "obj_traits::id_type"; db_type_id = member_database_type_id_.database_type_id (mi.m); // Handle NULL pointers and extract the id. // os << "{" << "typedef object_traits< " << class_fq_name (*c) << " > obj_traits;"; if (weak_pointer (mt)) { os << "typedef pointer_traits< " << mi.fq_type () << " > wptr_traits;" << "typedef pointer_traits< wptr_traits::" << "strong_pointer_type > ptr_traits;" << endl << "wptr_traits::strong_pointer_type sp (" << "wptr_traits::lock (" << member << "));"; member = "sp"; } else os << "typedef pointer_traits< " << mi.fq_type () << " > ptr_traits;" << endl; os << "bool is_null (ptr_traits::null_ptr (" << member << "));" << "if (!is_null)" << "{" << "const " << type << "& id (" << endl; if (lazy_pointer (mt)) os << "ptr_traits::object_id< ptr_traits::element_type > (" << member << ")"; else os << "obj_traits::id (ptr_traits::get_ref (" << member << "))"; os << ");" << endl; member = "id"; } else { type = mi.fq_type (); db_type_id = member_database_type_id_.database_type_id (mi.m); os << "{" << "bool is_null;"; } traits = "mssql::value_traits<\n " + type + ",\n " + db_type_id + " >"; } return true; } virtual void post (member_info& mi) { if (composite (mi.t)) os << "}"; else { // When handling a pointer, mi.t is the id type of the referenced // object. // if (object_pointer (member_utype (mi.m, key_prefix_))) { os << "}" << "else" << endl; if (!null (mi.m, key_prefix_)) os << "throw null_pointer ();"; else os << "i." << mi.var << "size_ind = SQL_NULL_DATA;"; } os << "}"; } } virtual void traverse_composite (member_info& mi) { os << traits << "::init (" << endl << "i." << mi.var << "value," << endl << member << "," << endl << "sk);"; } 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 (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 (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 (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 (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 (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;"; } private: string type; string db_type_id; string member; string traits; member_database_type_id member_database_type_id_; }; entry init_image_member_; // // init value // struct init_value_member: relational::init_value_member, member_base { init_value_member (base const& x) : member_base::base (x), // virtual base base (x), member_base (x), member_database_type_id_ (base::type_override_, base::fq_type_override_, base::key_prefix_) { } virtual bool pre (member_info& mi) { if (container (mi)) return false; if (!member_override_.empty ()) member = member_override_; else { string const& name (mi.m.name ()); member = "o." + name; if (mi.cq) member = "const_cast< " + mi.fq_type (false) + "& > (" + member + ")"; os << "// " << name << endl << "//" << endl; } // If this is a wrapped composite value, then we need to // "unwrap" it. For simple values this is taken care of // by the value_traits specializations. // if (mi.wrapper != 0 && composite (mi.t)) { // Here we need the wrapper type, not the wrapped type. // member = "wrapper_traits< " + mi.fq_type (false) + " >::" + "set_ref (\n" + member + ")"; } if (composite (mi.t)) traits = "composite_value_traits< " + mi.fq_type () + " >"; else { // When handling a pointer, mi.t is the id type of the referenced // object. // semantics::type& mt (member_utype (mi.m, key_prefix_)); if (semantics::class_* c = object_pointer (mt)) { type = "obj_traits::id_type"; db_type_id = member_database_type_id_.database_type_id (mi.m); // Handle NULL pointers and extract the id. // os << "{" << "typedef object_traits< " << class_fq_name (*c) << " > obj_traits;" << "typedef pointer_traits< " << mi.fq_type () << " > ptr_traits;" << endl << "if (i." << mi.var << "size_ind == SQL_NULL_DATA)" << endl; if (null (mi.m, key_prefix_)) os << member << " = ptr_traits::pointer_type ();"; else os << "throw null_pointer ();"; os << "else" << "{" << type << " id;"; member = "id"; } else { type = mi.fq_type (); db_type_id = member_database_type_id_.database_type_id (mi.m); } traits = "mssql::value_traits<\n " + type + ",\n " + db_type_id + " >"; } return true; } virtual void post (member_info& mi) { if (composite (mi.t)) return; // When handling a pointer, mi.t is the id type of the referenced // object. // semantics::type& mt (member_utype (mi.m, key_prefix_)); if (object_pointer (mt)) { if (!member_override_.empty ()) member = member_override_; else { member = "o." + mi.m.name (); if (mi.cq) member = "const_cast< " + mi.fq_type (false) + "& > (" + member + ")"; } if (lazy_pointer (mt)) os << member << " = ptr_traits::pointer_type (db, id);"; else os << "// If a compiler error points to the line below, then" << endl << "// it most likely means that a pointer used in a member" << endl << "// cannot be initialized from an object pointer." << endl << "//" << endl << member << " = ptr_traits::pointer_type (" << endl << "db.load< obj_traits::object_type > (id));"; os << "}" << "}"; } } virtual void traverse_composite (member_info& mi) { os << traits << "::init (" << endl << member << "," << endl << "i." << mi.var << "value," << endl << "db);" << endl; } 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 (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 (" << "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 (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; } private: string type; string db_type_id; string traits; string member; member_database_type_id member_database_type_id_; }; entry init_value_member_; struct statement_columns_common: context { void process (relational::statement_columns& cols, statement_kind sk) { using relational::statement_columns; // Long data columns must come last in the SELECT statement. // if (sk != statement_select) 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) { sql_type const& st (column_sql_type (*i->member, i->key_prefix)); bool l (false); // The same "short/long data" tests as in common.cxx. // switch (st.type) { case sql_type::CHAR: case sql_type::VARCHAR: case sql_type::BINARY: case sql_type::VARBINARY: { // Zero precision means max in VARCHAR(max). // if (st.prec == 0 || st.prec > options.mssql_short_limit ()) l = true; break; } case sql_type::NCHAR: case sql_type::NVARCHAR: { // Zero precision means max in NVARCHAR(max). Note that // the precision is in 2-byte UCS-2 characters, not bytes. // if (st.prec == 0 || st.prec * 2 > options.mssql_short_limit ()) l = true; break; } case sql_type::TEXT: case sql_type::NTEXT: case sql_type::IMAGE: { l = true; break; } default: break; } if (l) { 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_all_statement ().stream_result ();" << endl; } virtual void process_statement_columns (relational::statement_columns& cols, statement_kind sk) { statement_columns_common::process (cols, sk); } }; entry container_traits_; struct class_: relational::class_, statement_columns_common { class_ (base const& x): base (x) {} virtual void init_image_pre (type& c) { if (options.generate_query () && !(composite (c) || abstract (c))) os << "if (i.change_callback_.callback != 0)" << endl << "(i.change_callback_.callback) (i.change_callback_.context);" << endl; } virtual void init_value_extra () { os << "sts.find_statement ().stream_result ();"; } virtual void free_statement_result_immediate () { // Only free the result if there are no rows. Otherwise we // need to keep the result alive until after we are done // streaming long data. // os << "if (r == select_statement::no_data)" << endl << "st.free_result ();" << endl; } virtual void free_statement_result_delayed () { os << "sts.find_statement ().free_result ();"; } virtual void persist_statement_extra (type& c, relational::query_parameters&, persist_position p) { semantics::data_member* id (id_member (c)); if (id == 0 || !auto_ (*id)) return; // 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 ("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) os << endl << strlit ("; SELECT SCOPE_IDENTITY()"); return; } } if (p == persist_after_columns) os << strlit (" OUTPUT INSERTED." + column_qname (*id)) << endl; } virtual void process_statement_columns (relational::statement_columns& cols, statement_kind sk) { statement_columns_common::process (cols, sk); } }; entry class_entry_; } } }