From d3ec68714365046a30379a410cdff52e5f5ae066 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 10 Sep 2013 14:10:45 +0200 Subject: Schema versioning support --- odb/mssql/container-statements.hxx | 35 ++- odb/mssql/container-statements.txx | 5 +- odb/mssql/database.cxx | 9 +- odb/mssql/forward.hxx | 3 +- odb/mssql/makefile | 1 + odb/mssql/no-id-object-result.hxx | 6 +- odb/mssql/no-id-object-result.txx | 12 +- odb/mssql/no-id-object-statements.hxx | 1 + odb/mssql/polymorphic-object-result.hxx | 6 +- odb/mssql/polymorphic-object-result.txx | 28 ++- odb/mssql/polymorphic-object-statements.hxx | 23 +- odb/mssql/polymorphic-object-statements.txx | 16 +- odb/mssql/query.cxx | 16 +- odb/mssql/section-statements.hxx | 3 + odb/mssql/simple-object-result.hxx | 6 +- odb/mssql/simple-object-result.txx | 12 +- odb/mssql/simple-object-statements.hxx | 21 +- odb/mssql/simple-object-statements.txx | 19 +- odb/mssql/statement-cache.hxx | 5 +- odb/mssql/statement-cache.txx | 15 ++ odb/mssql/statement-processing.cxx | 348 ++++++++++++++++++++++++++++ odb/mssql/statement.cxx | 259 ++++++++++++++++----- odb/mssql/statement.hxx | 77 ++++-- odb/mssql/traits-calls.hxx | 191 +++++++++++++++ odb/mssql/view-result.hxx | 6 +- odb/mssql/view-result.txx | 12 +- 26 files changed, 998 insertions(+), 137 deletions(-) create mode 100644 odb/mssql/statement-processing.cxx create mode 100644 odb/mssql/traits-calls.hxx (limited to 'odb') diff --git a/odb/mssql/container-statements.hxx b/odb/mssql/container-statements.hxx index 8653afe..5a7e52c 100644 --- a/odb/mssql/container-statements.hxx +++ b/odb/mssql/container-statements.hxx @@ -10,6 +10,7 @@ #include // std::size_t #include +#include #include #include @@ -58,6 +59,14 @@ namespace odb return functions_; } + // Schema version. + // + const schema_version_migration& + version_migration () const {return *svm_;} + + void + version_migration (const schema_version_migration& svm) {svm_ = &svm;} + // Id image binding (external). // const binding& @@ -109,7 +118,13 @@ namespace odb if (insert_ == 0) insert_.reset ( new (details::shared) insert_statement_type ( - conn_, insert_text_, insert_image_binding_, false, false, false)); + conn_, + insert_text_, + versioned_, // Process if versioned. + insert_image_binding_, + false, + false, + false)); return *insert_; } @@ -120,7 +135,13 @@ namespace odb if (select_ == 0) select_.reset ( new (details::shared) select_statement_type ( - conn_, select_text_, id_binding_, select_image_binding_, false)); + conn_, + select_text_, + versioned_, // Process if versioned. + false, // Don't optimize. + id_binding_, + select_image_binding_, + false)); return *select_; } @@ -157,6 +178,9 @@ namespace odb const char* select_text_; const char* delete_text_; + bool versioned_; + const schema_version_migration* svm_; + details::shared_ptr insert_; details::shared_ptr select_; details::shared_ptr delete_; @@ -259,7 +283,12 @@ namespace odb if (update_ == 0) update_.reset ( new (details::shared) update_statement_type ( - this->conn_, update_text_, update_image_binding_, false, false)); + this->conn_, + update_text_, + this->versioned_, // Process if versioned. + update_image_binding_, + false, + false)); return *update_; } diff --git a/odb/mssql/container-statements.txx b/odb/mssql/container-statements.txx index 33c76fe..d989bfa 100644 --- a/odb/mssql/container-statements.txx +++ b/odb/mssql/container-statements.txx @@ -18,7 +18,8 @@ namespace odb id_binding_ (id), functions_ (this), insert_image_binding_ (0, 0), // Initialized by impl. - select_image_binding_ (0, 0) // Initialized by impl. + select_image_binding_ (0, 0), // Initialized by impl. + svm_ (0) { functions_.insert_ = &traits::insert; functions_.select_ = &traits::select; @@ -69,6 +70,8 @@ namespace odb this->insert_text_ = traits::insert_statement; this->select_text_ = traits::select_statement; this->delete_text_ = traits::delete_statement; + + this->versioned_ = traits::versioned; } // smart_container_statements_impl diff --git a/odb/mssql/database.cxx b/odb/mssql/database.cxx index b5e2fab..3c7075f 100644 --- a/odb/mssql/database.cxx +++ b/odb/mssql/database.cxx @@ -533,7 +533,14 @@ namespace odb : t.connection ()); try { - select_statement st (c, text.c_str (), param, result, false); + select_statement st (c, + text.c_str (), + false, // Don't process. + false, // Don't optimize. + param, + result, + false); + st.execute (); auto_result ar (st); diff --git a/odb/mssql/forward.hxx b/odb/mssql/forward.hxx index 124cdbe..8e5b06f 100644 --- a/odb/mssql/forward.hxx +++ b/odb/mssql/forward.hxx @@ -43,7 +43,8 @@ namespace odb { statement_select, statement_insert, - statement_update + statement_update, + statement_delete }; class binding; diff --git a/odb/mssql/makefile b/odb/mssql/makefile index 23b92cd..e713718 100644 --- a/odb/mssql/makefile +++ b/odb/mssql/makefile @@ -17,6 +17,7 @@ query-dynamic.cxx \ query-const-expr.cxx \ simple-object-statements.cxx \ statement.cxx \ +statement-processing.cxx \ statements-base.cxx \ tracer.cxx \ traits.cxx \ diff --git a/odb/mssql/no-id-object-result.hxx b/odb/mssql/no-id-object-result.hxx index 9bb726d..706e507 100644 --- a/odb/mssql/no-id-object-result.hxx +++ b/odb/mssql/no-id-object-result.hxx @@ -9,6 +9,7 @@ #include // std::size_t +#include #include #include @@ -16,6 +17,7 @@ #include #include // query_base #include +#include namespace odb { @@ -40,7 +42,8 @@ namespace odb no_id_object_result_impl (const query_base&, details::shared_ptr, - statements_type&); + statements_type&, + const schema_version_migration*); virtual void load (object_type&); @@ -68,6 +71,7 @@ namespace odb private: details::shared_ptr statement_; statements_type& statements_; + object_traits_calls tc_; bool can_load_; bool use_copy_; typename object_traits::image_type* image_copy_; diff --git a/odb/mssql/no-id-object-result.txx b/odb/mssql/no-id-object-result.txx index 6629fa1..1b9edd3 100644 --- a/odb/mssql/no-id-object-result.txx +++ b/odb/mssql/no-id-object-result.txx @@ -47,10 +47,12 @@ namespace odb no_id_object_result_impl:: no_id_object_result_impl (const query_base&, details::shared_ptr statement, - statements_type& statements) + statements_type& statements, + const schema_version_migration* svm) : base_type (statements.connection ()), statement_ (statement), statements_ (statements), + tc_ (svm), use_copy_ (false), image_copy_ (0) { @@ -65,9 +67,9 @@ namespace odb object_traits::callback (this->db_, obj, callback_event::pre_load); - object_traits::init (obj, - use_copy_ ? *image_copy_ : statements_.image (), - &this->db_); + tc_.init (obj, + use_copy_ ? *image_copy_ : statements_.image (), + &this->db_); // If we are using a copy, make sure the callback information for // long data also comes from the copy. @@ -100,7 +102,7 @@ namespace odb if (im.version != statements_.select_image_version ()) { binding& b (statements_.select_image_binding ()); - object_traits::bind (b.bind, im, statement_select); + tc_.bind (b.bind, im, statement_select); statements_.select_image_version (im.version); b.version++; } diff --git a/odb/mssql/no-id-object-statements.hxx b/odb/mssql/no-id-object-statements.hxx index adc071f..981bc9f 100644 --- a/odb/mssql/no-id-object-statements.hxx +++ b/odb/mssql/no-id-object-statements.hxx @@ -86,6 +86,7 @@ namespace odb new (details::shared) insert_statement_type ( conn_, object_traits::persist_statement, + object_traits::versioned, // Process if versioned. insert_image_binding_, false, object_traits::rowversion, diff --git a/odb/mssql/polymorphic-object-result.hxx b/odb/mssql/polymorphic-object-result.hxx index 91b5bfe..bb353b5 100644 --- a/odb/mssql/polymorphic-object-result.hxx +++ b/odb/mssql/polymorphic-object-result.hxx @@ -9,6 +9,7 @@ #include // std::size_t +#include #include #include @@ -16,6 +17,7 @@ #include #include // query_base #include +#include namespace odb { @@ -48,7 +50,8 @@ namespace odb polymorphic_object_result_impl (const query_base&, details::shared_ptr, - statements_type&); + statements_type&, + const schema_version_migration*); virtual void load (object_type*, bool fetch); @@ -82,6 +85,7 @@ namespace odb private: details::shared_ptr statement_; statements_type& statements_; + object_traits_calls tc_; bool can_load_; bool use_copy_; image_type* image_copy_; diff --git a/odb/mssql/polymorphic-object-result.txx b/odb/mssql/polymorphic-object-result.txx index 14c7c75..a9c78c1 100644 --- a/odb/mssql/polymorphic-object-result.txx +++ b/odb/mssql/polymorphic-object-result.txx @@ -41,7 +41,10 @@ namespace odb } if (!this->end_) + { statement_->free_result (); + this->end_ = true; + } statement_.reset (); } @@ -50,10 +53,12 @@ namespace odb polymorphic_object_result_impl:: polymorphic_object_result_impl (const query_base&, details::shared_ptr st, - statements_type& sts) + statements_type& sts, + const schema_version_migration* svm) : base_type (sts.connection ()), statement_ (st), statements_ (sts), + tc_ (svm), use_copy_ (false), image_copy_ (0) { @@ -134,7 +139,7 @@ namespace odb callback_event ce (callback_event::pre_load); pi.dispatch (info_type::call_callback, this->db_, pobj, &ce); - object_traits::init (*pobj, i, &this->db_); + tc_.init (*pobj, i, &this->db_); // If we are using a copy, make sure the callback information for // long data also comes from the copy. @@ -157,7 +162,7 @@ namespace odb idb.version++; } - object_traits::load_ (statements_, *pobj); + tc_.load_ (statements_, *pobj, false); // Load the dynamic part of the object unless static and dynamic // types are the same. @@ -168,7 +173,7 @@ namespace odb pi.dispatch (info_type::call_load, this->db_, pobj, &d); }; - rsts.load_delayed (); + rsts.load_delayed (tc_.version ()); l.unlock (); ce = callback_event::post_load; @@ -211,14 +216,16 @@ namespace odb typedef object_traits_impl traits; static void - rebind (typename traits::statements_type& sts) + rebind (typename traits::statements_type& sts, + const schema_version_migration* svm) { typename traits::image_type& im (sts.image ()); if (traits::check_version (sts.select_image_versions (), im)) { binding& b (sts.select_image_binding (traits::depth)); - traits::bind (b.bind, 0, 0, im, statement_select); + object_traits_calls tc (svm); + tc.bind (b.bind, 0, 0, im, statement_select); traits::update_version ( sts.select_image_versions (), im, sts.select_image_bindings ()); } @@ -233,14 +240,16 @@ namespace odb typedef object_traits_impl traits; static void - rebind (typename traits::statements_type& sts) + rebind (typename traits::statements_type& sts, + const schema_version_migration* svm) { typename traits::image_type& im (sts.image ()); if (im.version != sts.select_image_version ()) { binding& b (sts.select_image_binding ()); - traits::bind (b.bind, im, statement_select); + object_traits_calls tc (svm); + tc.bind (b.bind, im, statement_select); sts.select_image_version (im.version); b.version++; } @@ -264,7 +273,8 @@ namespace odb } use_copy_ = false; - polymorphic_image_rebind::rebind (statements_); + polymorphic_image_rebind::rebind ( + statements_, tc_.version ()); if (statement_->fetch () == select_statement::no_data) { diff --git a/odb/mssql/polymorphic-object-statements.hxx b/odb/mssql/polymorphic-object-statements.hxx index 2e435d9..381c607 100644 --- a/odb/mssql/polymorphic-object-statements.hxx +++ b/odb/mssql/polymorphic-object-statements.hxx @@ -101,6 +101,8 @@ namespace odb new (details::shared) select_statement_type ( this->conn_, object_traits::find_discriminator_statement, + false, // Doesn't need to be processed. + false, // Don't optimize. discriminator_id_image_binding_, discriminator_image_binding_, false)); @@ -114,6 +116,18 @@ namespace odb virtual ~polymorphic_root_object_statements (); + // Static "override" (statements type). + // + void + load_delayed (const schema_version_migration* svm) + { + assert (this->locked ()); + + if (!this->delayed_.empty ()) + this->template load_delayed_ ( + svm); + } + public: static const std::size_t id_column_count = object_statements::id_column_count; @@ -185,7 +199,10 @@ namespace odb // Delayed loading. // static void - delayed_loader (odb::database&, const id_type&, root_type&); + delayed_loader (odb::database&, + const id_type&, + root_type&, + const schema_version_migration*); public: // Root and immediate base statements. @@ -289,6 +306,7 @@ namespace odb new (details::shared) insert_statement_type ( conn_, object_traits::persist_statement, + object_traits::versioned, // Process if versioned. insert_image_binding_, false, false, @@ -308,6 +326,8 @@ namespace odb new (details::shared) select_statement_type ( conn_, object_traits::find_statements[i], + object_traits::versioned, // Process if versioned. + false, // Don't optimize. root_statements_.id_image_binding (), select_image_bindings_[i], false)); @@ -323,6 +343,7 @@ namespace odb new (details::shared) update_statement_type ( conn_, object_traits::update_statement, + object_traits::versioned, // Process if versioned. update_image_binding_, false, false)); diff --git a/odb/mssql/polymorphic-object-statements.txx b/odb/mssql/polymorphic-object-statements.txx index fa4756d..234af30 100644 --- a/odb/mssql/polymorphic-object-statements.txx +++ b/odb/mssql/polymorphic-object-statements.txx @@ -10,6 +10,7 @@ #include #include #include +#include namespace odb { @@ -102,7 +103,10 @@ namespace odb template void polymorphic_derived_object_statements:: - delayed_loader (odb::database& db, const id_type& id, root_type& robj) + delayed_loader (odb::database& db, + const id_type& id, + root_type& robj, + const schema_version_migration* svm) { connection_type& conn (transaction::current ().connection ()); polymorphic_derived_object_statements& sts ( @@ -113,18 +117,20 @@ namespace odb // The same code as in object_statements::load_delayed_(). // - if (!object_traits::find_ (sts, &id)) + object_traits_calls tc (svm); + + if (!tc.find_ (sts, &id)) throw object_not_persistent (); auto_result ar (*sts.find_[0]); object_traits::callback (db, obj, callback_event::pre_load); - object_traits::init (obj, sts.image (), &db); + tc.init (obj, sts.image (), &db); sts.find_[0]->stream_result (); ar.free (); - object_traits::load_ (sts, obj); // Load containers, etc. + tc.load_ (sts, obj, false); // Load containers, etc. - rsts.load_delayed (); + rsts.load_delayed (svm); { typename root_statements_type::auto_unlock u (rsts); diff --git a/odb/mssql/query.cxx b/odb/mssql/query.cxx index 10e97ba..faef6f7 100644 --- a/odb/mssql/query.cxx +++ b/odb/mssql/query.cxx @@ -98,8 +98,8 @@ namespace odb // We don't want extra spaces after '(' as well as before ',' // and ')'. // - if (last != ' ' && last != '(' && - first != ' ' && first != ',' && first != ')') + if (last != ' ' && last != '\n' && last != '(' && + first != ' ' && first != '\n' && first != ',' && first != ')') s += ' '; s += q; @@ -183,7 +183,7 @@ namespace odb // It either has to be an exact match, or there should be // a whitespace following the keyword. // - if (s.size () == n || s[n] == ' ' || s[n] =='\t') + if (s.size () == n || s[n] == ' ' || s[n] == '\n' || s[n] =='\t') return true; } @@ -242,7 +242,7 @@ namespace odb { case clause_part::kind_column: { - if (last != ' ' && last != '(') + if (last != ' ' && last != '\n' && last != '(') r += ' '; r += i->part; @@ -250,7 +250,7 @@ namespace odb } case clause_part::kind_param: { - if (last != ' ' && last != '(') + if (last != ' ' && last != '\n' && last != '(') r += ' '; // Add the conversion expression, if any. @@ -277,8 +277,8 @@ namespace odb const string& p (i->part); char first (!p.empty () ? p[0] : ' '); - if (last != ' ' && last != '(' && - first != ' ' && first != ',' && first != ')') + if (last != ' ' && last != '\n' && last != '(' && + first != ' ' && first != '\n' && first != ',' && first != ')') r += ' '; r += p; @@ -286,7 +286,7 @@ namespace odb } case clause_part::kind_bool: { - if (last != ' ' && last != '(') + if (last != ' ' && last != '\n' && last != '(') r += ' '; // SQL Server does not have "true" TRUE and FALSE boolean diff --git a/odb/mssql/section-statements.hxx b/odb/mssql/section-statements.hxx index bf26dba..d6ac777 100644 --- a/odb/mssql/section-statements.hxx +++ b/odb/mssql/section-statements.hxx @@ -97,6 +97,8 @@ namespace odb new (details::shared) select_statement_type ( conn_, traits::select_statement, + traits::versioned, // Process if versioned. + false, // Don't optimize. id_binding_, select_image_binding_, false)); @@ -112,6 +114,7 @@ namespace odb new (details::shared) update_statement_type ( conn_, traits::update_statement, + traits::versioned, // Process if versioned. update_image_binding_, traits::rowversion, false)); diff --git a/odb/mssql/simple-object-result.hxx b/odb/mssql/simple-object-result.hxx index 4240c29..e719db2 100644 --- a/odb/mssql/simple-object-result.hxx +++ b/odb/mssql/simple-object-result.hxx @@ -9,6 +9,7 @@ #include // std::size_t +#include #include #include @@ -16,6 +17,7 @@ #include #include // query_base #include +#include namespace odb { @@ -41,7 +43,8 @@ namespace odb object_result_impl (const query_base&, details::shared_ptr, - statements_type&); + statements_type&, + const schema_version_migration*); virtual void load (object_type&, bool fetch); @@ -72,6 +75,7 @@ namespace odb private: details::shared_ptr statement_; statements_type& statements_; + object_traits_calls tc_; bool can_load_; bool use_copy_; typename object_traits::image_type* image_copy_; diff --git a/odb/mssql/simple-object-result.txx b/odb/mssql/simple-object-result.txx index a7fb191..510494a 100644 --- a/odb/mssql/simple-object-result.txx +++ b/odb/mssql/simple-object-result.txx @@ -49,10 +49,12 @@ namespace odb object_result_impl:: object_result_impl (const query_base&, details::shared_ptr statement, - statements_type& statements) + statements_type& statements, + const schema_version_migration* svm) : base_type (statements.connection ()), statement_ (statement), statements_ (statements), + tc_ (svm), use_copy_ (false), image_copy_ (0) { @@ -75,7 +77,7 @@ namespace odb typename object_traits::image_type& i ( use_copy_ ? *image_copy_ : statements_.image ()); - object_traits::init (obj, i, &this->db_); + tc_.init (obj, i, &this->db_); // If we are using a copy, make sure the callback information for // long data also comes from the copy. @@ -98,8 +100,8 @@ namespace odb idb.version++; } - object_traits::load_ (statements_, obj); - statements_.load_delayed (); + tc_.load_ (statements_, obj, false); + statements_.load_delayed (tc_.version ()); l.unlock (); object_traits::callback (this->db_, obj, callback_event::post_load); } @@ -134,7 +136,7 @@ namespace odb if (im.version != statements_.select_image_version ()) { binding& b (statements_.select_image_binding ()); - object_traits::bind (b.bind, im, statement_select); + tc_.bind (b.bind, im, statement_select); statements_.select_image_version (im.version); b.version++; } diff --git a/odb/mssql/simple-object-statements.hxx b/odb/mssql/simple-object-statements.hxx index 9cbd69c..ab43439 100644 --- a/odb/mssql/simple-object-statements.hxx +++ b/odb/mssql/simple-object-statements.hxx @@ -243,8 +243,10 @@ namespace odb // Delayed loading. // - typedef void (*loader_function) ( - odb::database&, const id_type&, object_type&); + typedef void (*loader_function) (odb::database&, + const id_type&, + object_type&, + const schema_version_migration*); void delay_load (const id_type& id, @@ -256,12 +258,12 @@ namespace odb } void - load_delayed () + load_delayed (const schema_version_migration* svm) { assert (locked ()); if (!delayed_.empty ()) - load_delayed_ (); + load_delayed_ (svm); } void @@ -349,6 +351,7 @@ namespace odb new (details::shared) insert_statement_type ( conn_, object_traits::persist_statement, + object_traits::versioned, // Process if versioned. insert_image_binding_, object_traits::auto_id, object_traits::rowversion, @@ -365,6 +368,8 @@ namespace odb new (details::shared) select_statement_type ( conn_, object_traits::find_statement, + object_traits::versioned, // Process if versioned. + false, // Don't optimize. id_image_binding_, select_image_binding_, false)); @@ -380,6 +385,7 @@ namespace odb new (details::shared) update_statement_type ( conn_, object_traits::update_statement, + object_traits::versioned, // Process if versioned. update_image_binding_, object_traits::rowversion, false)); @@ -458,14 +464,15 @@ namespace odb object_statements (const object_statements&); object_statements& operator= (const object_statements&); - private: + protected: + template void - load_delayed_ (); + load_delayed_ (const schema_version_migration*); void clear_delayed_ (); - private: + protected: template friend class polymorphic_derived_object_statements; diff --git a/odb/mssql/simple-object-statements.txx b/odb/mssql/simple-object-statements.txx index 989f178..84ed5db 100644 --- a/odb/mssql/simple-object-statements.txx +++ b/odb/mssql/simple-object-statements.txx @@ -8,6 +8,7 @@ #include #include +#include namespace odb { @@ -67,8 +68,9 @@ namespace odb } template + template void object_statements:: - load_delayed_ () + load_delayed_ (const schema_version_migration* svm) { database& db (connection ().database ()); @@ -83,7 +85,9 @@ namespace odb if (l.loader == 0) { - if (!object_traits::find_ (*this, &l.id)) + object_traits_calls tc (svm); + + if (!tc.find_ (static_cast (*this), &l.id)) throw object_not_persistent (); // Our find_() version delays result freeing. @@ -96,13 +100,16 @@ namespace odb // loads being added to the delayed_ vector. We need to process // those before we call the post callback. // - object_traits::init (*l.obj, image (), &db); + tc.init (*l.obj, image (), &db); find_->stream_result (); ar.free (); - object_traits::load_ (*this, *l.obj); // Load containers, etc. + + // Load containers, etc. + // + tc.load_ (static_cast (*this), *l.obj, false); if (!delayed_.empty ()) - load_delayed_ (); + load_delayed_ (svm); // Temporarily unlock the statement for the post_load call so that // it can load objects of this type recursively. This is safe to do @@ -118,7 +125,7 @@ namespace odb } } else - l.loader (db, l.id, *l.obj); + l.loader (db, l.id, *l.obj, svm); pointer_cache_traits::load (ig.position ()); ig.release (); diff --git a/odb/mssql/statement-cache.hxx b/odb/mssql/statement-cache.hxx index 5754ba3..b09741f 100644 --- a/odb/mssql/statement-cache.hxx +++ b/odb/mssql/statement-cache.hxx @@ -29,7 +29,9 @@ namespace odb class LIBODB_MSSQL_EXPORT statement_cache { public: - statement_cache (connection& conn): conn_ (conn) {} + statement_cache (connection& conn) + : conn_ (conn), + version_seq_ (conn.database ().schema_version_sequence ()) {} template typename object_traits_impl::statements_type& @@ -45,6 +47,7 @@ namespace odb details::type_info_comparator> map; connection& conn_; + unsigned int version_seq_; map map_; }; } diff --git a/odb/mssql/statement-cache.txx b/odb/mssql/statement-cache.txx index 5ab2918..7840227 100644 --- a/odb/mssql/statement-cache.txx +++ b/odb/mssql/statement-cache.txx @@ -2,6 +2,8 @@ // copyright : Copyright (c) 2005-2013 Code Synthesis Tools CC // license : ODB NCUEL; see accompanying LICENSE file +#include + namespace odb { namespace mssql @@ -15,6 +17,16 @@ namespace odb typename object_traits_impl::statements_type statements_type; + // Clear the cache if the database version has changed. This + // makes sure we don't re-use statements that correspond to + // the old schema. + // + if (version_seq_ != conn_.database ().schema_version_sequence ()) + { + map_.clear (); + version_seq_ = conn_.database ().schema_version_sequence (); + } + map::iterator i (map_.find (&typeid (T))); if (i != map_.end ()) @@ -31,6 +43,9 @@ namespace odb view_statements& statement_cache:: find_view () { + // We don't cache any statements for views so no need to clear + // the cache. + map::iterator i (map_.find (&typeid (T))); if (i != map_.end ()) diff --git a/odb/mssql/statement-processing.cxx b/odb/mssql/statement-processing.cxx new file mode 100644 index 0000000..e591385 --- /dev/null +++ b/odb/mssql/statement-processing.cxx @@ -0,0 +1,348 @@ +// file : odb/mssql/statement-processing.cxx +// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include + +#include + +#ifdef LIBODB_TRACE_STATEMENT_PROCESSING +# include +#endif + +#include + +using namespace std; + +namespace odb +{ + namespace mssql + { + typedef bind bind_type; + + void statement:: + process_select (const char* s, + const bind_type* bind, + std::size_t bind_size, +#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING + bool optimize, +#else + bool, +#endif + std::string& r) + { + // This implementation is pretty much the same as the generic one + // except for two things: + // + // 1. When checking for the fast case, take into account long data + // columns which we may have to re-arrange. + // + // 2. Create the column list in two passes, ordinary columns first + // followed by the long data columns. + // + + bool empty (true); // Empty case (if none present). + bool fast (true); // Fast case (if all present and none are long data). + for (size_t i (0); i != bind_size && (empty || fast); ++i) + { + const bind_type& b (bind[i]); + + if (b.buffer != 0) + empty = false; + else + fast = false; + + if (b.type == bind_type::long_string || + b.type == bind_type::long_nstring || + b.type == bind_type::long_binary) + fast = false; + } + + // Empty. + // + if (empty) + { + r.clear (); + +#ifdef LIBODB_TRACE_STATEMENT_PROCESSING + if (*s != '\0') + cerr << endl + << "old: '" << s << "'" << endl << endl + << "new: '" << r << "'" << endl << endl; +#endif + return; + } + + // Fast path: just remove the "structure". + // +#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING + if (fast && !optimize) + { + process_fast (s, r); + return; + } +#endif + + // Scan the statement and store the positions of various parts. + // + size_t n (traits::length (s)); + const char* e (s + n); + + // Header. + // + const char* p (find (s, e, '\n')); + assert (p != 0); + size_t header_size (p - s); + p++; + + // Column list. + // + const char* columns_begin (p); + for (const char* ce (comma_begin (p, e)); ce != 0; comma_next (p, ce, e)) + ; + + // FROM. + assert (traits::compare (p, "FROM ", 5) == 0); + const char* from_begin (p); + p = find (p, e, '\n'); // May not end with '\n'. + if (p == 0) + p = e; + size_t from_size (p - from_begin); + if (p != e) + p++; + + // JOIN list. + // + const char* joins_begin (0), *joins_end (0); + if (e - p > 5 && traits::compare (p, "LEFT JOIN ", 10) == 0) + { + joins_begin = p; + + // Find the end of the JOIN list. + // + for (const char* je (newline_begin (p, e)); + je != 0; newline_next (p, je, e, "LEFT JOIN ", 10)) + ; + + joins_end = (p != e ? p - 1 : p); + } + +#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING + if (fast && joins_begin == 0) + { + // No JOINs to optimize so can still take the fast path. + // + process_fast (s, r); + return; + } +#endif + + // Trailer (WHERE, ORDER BY, etc). + // + const char* trailer_begin (0); + size_t trailer_size (0); + if (e - p != 0) + { + trailer_begin = p; + trailer_size = e - p; + } + + // Assume the same size as the original. It can only shrink, and in + // most cases only slightly. So this is a good approximation. + // + r.reserve (n); + r.assign (s, header_size); + + // Column list. + // + { + r += ' '; + + size_t i (0); + bool need_second (false); + + // First pass: non-long data columns. + // + { + size_t bi (0); + for (const char *c (columns_begin), *ce (comma_begin (c, e)); + ce != 0; comma_next (c, ce, e)) + { + const bind_type& b (bind[bi++]); + + // See if the column is present in the bind array and if it + // is of the right kind. + // + if (b.buffer == 0) + continue; + + if (b.type == bind_type::long_string || + b.type == bind_type::long_nstring || + b.type == bind_type::long_binary) + { + need_second = true; + continue; + } + + // Append the column. + // + if (i++ != 0) + r += ", "; // Add the space for consistency with the fast path. + + r.append (c, ce - c); + } + } + + // Second pass: long data columns. + // + if (need_second) + { + size_t bi (0); + for (const char *c (columns_begin), *ce (comma_begin (c, e)); + ce != 0; comma_next (c, ce, e)) + { + const bind_type& b (bind[bi++]); + + // See if the column is present in the bind array and if it + // is of the right kind. + // + if (b.buffer == 0 || + (b.type != bind_type::long_string && + b.type != bind_type::long_nstring && + b.type != bind_type::long_binary)) + continue; + + // Append the column. + // + if (i++ != 0) + r += ", "; // Add the space for consistency with the fast path. + + r.append (c, ce - c); + } + } + } + + // From. + // + r += ' '; + r.append (from_begin, from_size); + + // JOIN list, pass 1. + // + size_t join_pos (0); + if (joins_begin != 0) + { + // Fill in the JOIN "area" with spaces. + // + r.resize (r.size () + joins_end - joins_begin + 1, ' '); + join_pos = r.size () + 1; // End of the last JOIN. + } + + // Trailer. + // + if (trailer_size != 0) + { + r += ' '; + r.append (trailer_begin, trailer_size); + } + + // JOIN list, pass 2. + // + if (joins_begin != 0) + { + // Splice the JOINs into the pre-allocated area. + // + for (const char* je (joins_end), *j (newline_rbegin (je, joins_begin)); + j != 0; newline_rnext (j, je, joins_begin)) + { + size_t n (je - j); + + // Get the alias or, if none used, the table name. + // + p = find (j + 10, je, ' '); // 10 for "LEFT JOIN ". + const char* table_end (p); + p++; // Skip space. + + // We may or may not have the AS keyword. + // + const char* alias_begin (0); + size_t alias_size (0); + if (je - p > 3 && traits::compare (p, "ON ", 3) != 0) + { + p += 3; + alias_begin = p; + alias_size = find (p, je, ' ') - alias_begin; + } + else + { + alias_begin = j + 10; + alias_size = table_end - alias_begin; + } + + // The alias must be quoted. + // + assert (*alias_begin == '[' && + *(alias_begin + alias_size - 1) == ']'); + + // We now need to see if the alias is used in either the SELECT + // list, the WHERE conditions, or the ON condition of any of the + // JOINs that we have already processed and decided to keep. + // + // Instead of re-parsing the whole thing again, we are going to + // take a shortcut and simply search for the alias in the statement + // we have constructed so far (that's why we have have added the + // trailer before filling in the JOINs). To make it more robust, + // we are going to do a few extra sanity checks, specifically, + // that the alias is a top level identifier and is followed by + // only a single identifer (column). This will catch cases like + // [s].[t].[c] where [s] is also used as an alias or LEFT JOIN [t] + // where [t] is also used as an alias in another JOIN. + // + bool found (false); + for (size_t p (r.find (alias_begin, 0, alias_size)); + p != string::npos; + p = r.find (alias_begin, p + alias_size, alias_size)) + { + size_t e (p + alias_size); + + // If we are not a top-level qualifier or not a bottom-level, + // then we are done (3 is for at least "[a]"). + // + if ((p != 0 && r[p - 1] == '.') || + (e + 3 >= r.size () || (r[e] != '.' || r[e + 1] != '['))) + continue; + + // The only way to distinguish the [a].[c] from FROM [a].[c] or + // LEFT JOIN [a].[c] is by checking the prefix. + // + if ((p > 5 && r.compare (p - 5, 5, "FROM ") == 0) || + (p > 10 && r.compare (p - 10, 10, "LEFT JOIN ") == 0)) + continue; + + // Check that we are followed by a single identifier. + // + e = r.find (']', e + 2); + if (e == string::npos || (e + 1 != r.size () && r[e + 1] == '.')) + continue; + + found = true; + break; + } + + join_pos -= n + 1; // Extra one for space. + if (found) + r.replace (join_pos, n, j, n); + else + r.erase (join_pos - 1, n + 1); // Extra one for space. + } + } + +#ifdef LIBODB_TRACE_STATEMENT_PROCESSING + if (r.size () != n) + cerr << endl + << "old: '" << s << "'" << endl << endl + << "new: '" << r << "'" << endl << endl; +#endif + } + } +} diff --git a/odb/mssql/statement.cxx b/odb/mssql/statement.cxx index 4283b55..e2022fa 100644 --- a/odb/mssql/statement.cxx +++ b/odb/mssql/statement.cxx @@ -95,19 +95,36 @@ namespace odb // statement:: - statement (connection_type& conn, const string& text) - : conn_ (conn), text_copy_ (text), text_ (text_copy_.c_str ()) + statement (connection_type& conn, + const string& text, + statement_kind sk, + const binding* process, + bool optimize) + : conn_ (conn) { - init (text_copy_.size ()); + if (process == 0) + { + text_copy_ = text; + text_ = text_copy_.c_str (); + } + else + text_ = text.c_str (); // Temporary, see init(). + + init (text.size (), sk, process, optimize); } statement:: - statement (connection_type& conn, const char* text, bool copy) + statement (connection_type& conn, + const char* text, + statement_kind sk, + const binding* process, + bool optimize, + bool copy) : conn_ (conn) { size_t n; - if (copy) + if (process == 0 && copy) { text_copy_ = text; text_ = text_copy_.c_str (); @@ -116,15 +133,53 @@ namespace odb else { text_ = text; - n = strlen (text_); + n = strlen (text_); // Potentially temporary, see init(). } - init (n); + init (n, sk, process, optimize); } void statement:: - init (size_t text_size) + init (size_t text_size, + statement_kind sk, + const binding* proc, + bool optimize) { + if (proc != 0) + { + switch (sk) + { + case statement_select: + process_select (text_, + proc->bind, proc->count, + optimize, + text_copy_); + break; + case statement_insert: + process_insert (text_, + &proc->bind->buffer, proc->count, sizeof (bind), + '?', + text_copy_); + break; + case statement_update: + process_update (text_, + &proc->bind->buffer, proc->count, sizeof (bind), + '?', + text_copy_); + break; + case statement_delete: + assert (false); + } + + text_ = text_copy_.c_str (); + text_size = text_copy_.size (); + } + + // Empty statement. + // + if (*text_ == '\0') + return; + SQLRETURN r; // Allocate the handle. @@ -188,10 +243,14 @@ namespace odb { SQLRETURN r; - n++; // Parameters are counted from 1. - - for (size_t i (1); i < n; ++i, ++b) + SQLUSMALLINT i (0); + for (bind* end (b + n); b != end; ++b) { + if (b->buffer == 0) // Skip NULL entries. + continue; + + i++; // Column index is 1-based. + SQLULEN col_size (0); SQLSMALLINT digits (0); SQLPOINTER buf; @@ -342,7 +401,7 @@ namespace odb r = SQLBindParameter ( stmt_, - (SQLUSMALLINT) i, + i, SQL_PARAM_INPUT, c_type_lookup[b->type], sql_type_lookup[b->type], @@ -357,14 +416,18 @@ namespace odb } } - size_t statement:: - bind_result (bind* b, size_t n) + SQLUSMALLINT statement:: + bind_result (bind* b, size_t n, SQLUSMALLINT& long_count) { + long_count = 0; SQLRETURN r; - size_t i (0); + SQLUSMALLINT i (0); for (bind* end (b + n); b != end; ++b) { + if (b->buffer == 0) // Skip NULL entries. + continue; + SQLLEN cap (0); switch (b->type) @@ -428,6 +491,7 @@ namespace odb { // Long data is not bound. // + long_count++; continue; } case bind::date: @@ -468,7 +532,7 @@ namespace odb } r = SQLBindCol (stmt_, - (SQLUSMALLINT) (i + 1), // Results are counted from 1. + ++i, // Column index is 1-based. c_type_lookup[b->type], (SQLPOINTER) b->buffer, cap, @@ -476,8 +540,6 @@ namespace odb if (!SQL_SUCCEEDED (r)) translate_error (r, conn_, stmt_); - - ++i; } return i; @@ -557,7 +619,7 @@ namespace odb } void statement:: - stream_result (bind* b, size_t i, size_t n, void* obase, void* nbase) + stream_result (SQLUSMALLINT i, bind* b, size_t n, void* obase, void* nbase) { details::buffer& tmp_buf (conn_.long_data_buffer ()); @@ -568,8 +630,10 @@ namespace odb for (bind* end (b + n); b != end; ++b) { - bool char_data; + if (b->buffer == 0) // Skip NULL entries. + continue; + bool char_data; switch (b->type) { case bind::long_string: @@ -612,7 +676,7 @@ namespace odb // SQLLEN si; r = SQLGetData (stmt_, - (SQLUSMALLINT) (i + 1), + ++i, c_type_lookup[b->type], tmp_buf.data (), // Dummy value. 0, @@ -655,7 +719,7 @@ namespace odb // contain a valid value. // r = SQLGetData (stmt_, - (SQLUSMALLINT) (i + 1), + i, c_type_lookup[b->type], (SQLPOINTER) buf, (SQLLEN) size, @@ -686,8 +750,6 @@ namespace odb if (size_left != 0) size_left -= size; } - - ++i; } } @@ -701,42 +763,74 @@ namespace odb select_statement:: select_statement (connection_type& conn, - const string& t, + const string& text, + bool process, + bool optimize, binding& param, binding& result) - : statement (conn, t), result_ (result) + : statement (conn, + text, statement_select, + (process ? &result : 0), optimize), + result_ (result) { - bind_param (param.bind, param.count); - first_long_ = bind_result (result.bind, result.count); + if (!empty ()) + { + bind_param (param.bind, param.count); + result_count_ = bind_result (result.bind, result.count, long_count_); + } } select_statement:: select_statement (connection_type& conn, - const char* t, + const char* text, + bool process, + bool optimize, binding& param, binding& result, - bool ct) - : statement (conn, t, ct), result_ (result) + bool copy_text) + : statement (conn, + text, statement_select, + (process ? &result : 0), optimize, + copy_text), + result_ (result) { - bind_param (param.bind, param.count); - first_long_ = bind_result (result.bind, result.count); + if (!empty ()) + { + bind_param (param.bind, param.count); + result_count_ = bind_result (result.bind, result.count, long_count_); + } } select_statement:: - select_statement (connection_type& conn, const string& t, binding& result) - : statement (conn, t), result_ (result) + select_statement (connection_type& conn, + const string& text, + bool process, + bool optimize, + binding& result) + : statement (conn, + text, statement_select, + (process ? &result : 0), optimize), + result_ (result) { - first_long_ = bind_result (result.bind, result.count); + if (!empty ()) + result_count_ = bind_result (result.bind, result.count, long_count_); } select_statement:: select_statement (connection_type& conn, - const char* t, + const char* text, + bool process, + bool optimize, binding& result, - bool ct) - : statement (conn, t, ct), result_ (result) + bool copy_text) + : statement (conn, + text, statement_select, + (process ? &result : 0), optimize, + copy_text), + result_ (result) { - first_long_ = bind_result (result.bind, result.count); + if (!empty ()) + result_count_ = bind_result (result.bind, result.count, long_count_); } void select_statement:: @@ -746,6 +840,21 @@ namespace odb if (!SQL_SUCCEEDED (r)) translate_error (r, conn_, stmt_); + +#ifndef NDEBUG + SQLSMALLINT cols; + r = SQLNumResultCols (stmt_, &cols); + + if (!SQL_SUCCEEDED (r)) + translate_error (r, conn_, stmt_); + + // Make sure that the number of columns in the result returned by + // the database matches the number that we expect. A common cause + // of this assertion is a native view with a number of data members + // not matching the number of columns in the SELECT-list. + // + assert (static_cast (cols) == result_count_ + long_count_); +#endif } select_statement::result select_statement:: @@ -793,11 +902,14 @@ namespace odb insert_statement:: insert_statement (connection_type& conn, - const string& t, + const string& text, + bool process, binding& param, bool returning_id, bool returning_version) - : statement (conn, t), + : statement (conn, + text, statement_insert, + (process ? ¶m : 0), false), returning_id_ (returning_id), returning_version_ (returning_version) { @@ -809,12 +921,16 @@ namespace odb insert_statement:: insert_statement (connection_type& conn, - const char* t, + const char* text, + bool process, binding& param, bool returning_id, bool returning_version, - bool ct) - : statement (conn, t, ct), + bool copy_text) + : statement (conn, + text, statement_insert, + (process ? ¶m : 0), false, + copy_text), returning_id_ (returning_id), returning_version_ (returning_version) { @@ -980,30 +1096,44 @@ namespace odb update_statement:: update_statement (connection_type& conn, - const string& t, + const string& text, + bool process, binding& param, bool returning_version) - : statement (conn, t), returning_version_ (returning_version) + : statement (conn, + text, statement_update, + (process ? ¶m : 0), false), + returning_version_ (returning_version) { - bind_param (param.bind, param.count); + if (!empty ()) + { + bind_param (param.bind, param.count); - if (returning_version_) - init_result (); + if (returning_version_) + init_result (); + } } update_statement:: update_statement (connection_type& conn, - const char* t, + const char* text, + bool process, binding& param, bool returning_version, - bool ct) - : statement (conn, t, ct), + bool copy_text) + : statement (conn, + text, statement_update, + (process ? ¶m : 0), false, + copy_text), returning_version_ (returning_version) { - bind_param (param.bind, param.count); + if (!empty ()) + { + bind_param (param.bind, param.count); - if (returning_version_) - init_result (); + if (returning_version_) + init_result (); + } } void update_statement:: @@ -1080,18 +1210,25 @@ namespace odb } delete_statement:: - delete_statement (connection_type& conn, const string& t, binding& param) - : statement (conn, t) + delete_statement (connection_type& conn, + const string& text, + binding& param) + : statement (conn, + text, statement_delete, + 0, false) { bind_param (param.bind, param.count); } delete_statement:: delete_statement (connection_type& conn, - const char* t, + const char* text, binding& param, - bool ct) - : statement (conn, t, ct) + bool copy_text) + : statement (conn, + text, statement_delete, + 0, false, + copy_text) { bind_param (param.bind, param.count); } diff --git a/odb/mssql/statement.hxx b/odb/mssql/statement.hxx index add996c..1c012ce 100644 --- a/odb/mssql/statement.hxx +++ b/odb/mssql/statement.hxx @@ -50,20 +50,58 @@ namespace odb return conn_; } + // A statement can be empty. This is used to handle situations + // where a SELECT or UPDATE statement ends up not having any + // columns after processing. An empty statement cannot be + // executed. + // + bool + empty () const + { + return stmt_ == 0; + } + protected: - statement (connection_type&, const std::string& text); - statement (connection_type&, const char* text, bool copy_text); + // We keep two versions to take advantage of std::string COW. + // + statement (connection_type&, + const std::string& text, + statement_kind, + const binding* process, + bool optimize); + + statement (connection_type&, + const char* text, + statement_kind, + const binding* process, + bool optimize, + bool copy_text); private: void - init (std::size_t text_size); + init (std::size_t text_size, + statement_kind, + const binding* process, + bool optimize); + + // Custom implementation for SQL Server that also moves long data + // columns to the end. + // + static void + process_select (const char* statement, + const bind*, + std::size_t bind_size, + bool optimize, + std::string& result); protected: void bind_param (bind*, std::size_t count); - std::size_t - bind_result (bind*, std::size_t count); + // Return the actual number of columns bound. + // + SQLUSMALLINT + bind_result (bind*, std::size_t count, SQLUSMALLINT& long_count); SQLRETURN execute (); @@ -75,8 +113,8 @@ namespace odb // instead of the bound image. // void - stream_result (bind*, - std::size_t start, + stream_result (SQLUSMALLINT start_col, + bind*, std::size_t count, void* old_base = 0, void* new_base = 0); @@ -100,21 +138,29 @@ namespace odb // select_statement (connection_type& conn, const std::string& text, + bool process_text, + bool optimize_text, binding& param, binding& result); select_statement (connection_type& conn, const char* text, + bool process_text, + bool optimize_text, binding& param, binding& result, bool copy_text = true); select_statement (connection_type& conn, const std::string& text, + bool process_text, + bool optimize_text, binding& result); select_statement (connection_type& conn, const char* text, + bool process_text, + bool optimize_text, binding& result, bool copy_text = true); @@ -135,15 +181,13 @@ namespace odb bool stream_result (void* old_base = 0, void* new_base = 0) { - bool ld (first_long_ != result_.count); - - if (ld) - statement::stream_result (result_.bind, - first_long_, + if (long_count_ != 0) + statement::stream_result (result_count_, + result_.bind, result_.count, old_base, new_base); - return ld; + return long_count_ != 0; } void @@ -155,7 +199,8 @@ namespace odb private: binding& result_; - std::size_t first_long_; // First long data column. + SQLUSMALLINT result_count_; // Actual number of columns bound. + SQLUSMALLINT long_count_; // Number of long data columns. }; struct LIBODB_MSSQL_EXPORT auto_result @@ -199,12 +244,14 @@ namespace odb insert_statement (connection_type& conn, const std::string& text, + bool process_text, binding& param, bool returning_id, bool returning_version); insert_statement (connection_type& conn, const char* text, + bool process_text, binding& param, bool returning_id, bool returning_version, @@ -253,11 +300,13 @@ namespace odb update_statement (connection_type& conn, const std::string& text, + bool process, binding& param, bool returning_version); update_statement (connection_type& conn, const char* text, + bool process, binding& param, bool returning_version, bool copy_text = true); diff --git a/odb/mssql/traits-calls.hxx b/odb/mssql/traits-calls.hxx new file mode 100644 index 0000000..88665f7 --- /dev/null +++ b/odb/mssql/traits-calls.hxx @@ -0,0 +1,191 @@ +// file : odb/mssql/traits-calls.hxx +// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_MSSQL_TRAITS_CALLS_HXX +#define ODB_MSSQL_TRAITS_CALLS_HXX + +#include + +#include // std::size_t + +#include +#include +#include + +#include +#include + +namespace odb +{ + namespace mssql + { + // + // object_traits_calls + // + + template ::versioned> + struct object_traits_calls; + + template + struct object_traits_calls + { + typedef object_traits_impl traits; + typedef typename traits::image_type image_type; + typedef mssql::bind bind_type; + + object_traits_calls (const schema_version_migration*) {} + + const schema_version_migration* + version () const {return 0;} + + static void + bind (bind_type* b, image_type& i, statement_kind sk) + { + traits::bind (b, i, sk); + } + + // Poly-derived version. + // + static void + bind (bind_type* b, + const bind_type* id, std::size_t id_size, + image_type& i, + statement_kind sk) + { + traits::bind (b, id, id_size, i, sk); + } + + static void + init (T& o, const image_type& i, odb::database* db) + { + traits::init (o, i, db); + } + + static bool + find_ (typename traits::statements_type& sts, + const typename traits::id_type* id) + { + return traits::find_ (sts, id); + } + + static void + load_ (typename traits::statements_type& sts, T& o, bool reload) + { + return traits::load_ (sts, o, reload); + } + }; + + template + struct object_traits_calls + { + typedef object_traits_impl traits; + typedef typename traits::image_type image_type; + typedef mssql::bind bind_type; + + object_traits_calls (const schema_version_migration* svm): svm_ (*svm) {} + + const schema_version_migration* + version () const {return &svm_;} + + void + bind (bind_type* b, image_type& i, statement_kind sk) const + { + traits::bind (b, i, sk, svm_); + } + + // Poly-derived version. + // + void + bind (bind_type* b, + const bind_type* id, std::size_t id_size, + image_type& i, + statement_kind sk) const + { + traits::bind (b, id, id_size, i, sk, svm_); + } + + void + init (T& o, const image_type& i, odb::database* db) const + { + traits::init (o, i, db, svm_); + } + + bool + find_ (typename traits::statements_type& sts, + const typename traits::id_type* id) const + { + return traits::find_ (sts, id, svm_); + } + + void + load_ (typename traits::statements_type& sts, T& o, bool reload) const + { + return traits::load_ (sts, o, reload, svm_); + } + + private: + const schema_version_migration& svm_; + }; + + // + // view_traits_calls + // + + template ::versioned> + struct view_traits_calls; + + template + struct view_traits_calls + { + typedef view_traits_impl traits; + typedef typename traits::image_type image_type; + typedef mssql::bind bind_type; + + view_traits_calls (const schema_version_migration*) {} + + static void + bind (bind_type* b, image_type& i) + { + traits::bind (b, i); + } + + static void + init (T& o, const image_type& i, odb::database* db) + { + traits::init (o, i, db); + } + }; + + template + struct view_traits_calls + { + typedef view_traits_impl traits; + typedef typename traits::image_type image_type; + typedef mssql::bind bind_type; + + view_traits_calls (const schema_version_migration* svm): svm_ (*svm) {} + + void + bind (bind_type* b, image_type& i) const + { + traits::bind (b, i, svm_); + } + + void + init (T& o, const image_type& i, odb::database* db) const + { + traits::init (o, i, db, svm_); + } + + private: + const schema_version_migration& svm_; + }; + } +} + +#include + +#endif // ODB_MSSQL_TRAITS_CALLS_HXX diff --git a/odb/mssql/view-result.hxx b/odb/mssql/view-result.hxx index 0d96ce0..472d2d4 100644 --- a/odb/mssql/view-result.hxx +++ b/odb/mssql/view-result.hxx @@ -9,6 +9,7 @@ #include // std::size_t +#include #include #include @@ -16,6 +17,7 @@ #include #include // query_base, view_statements #include +#include namespace odb { @@ -40,7 +42,8 @@ namespace odb view_result_impl (const query_base&, details::shared_ptr, - statements_type&); + statements_type&, + const schema_version_migration*); virtual void load (view_type&); @@ -68,6 +71,7 @@ namespace odb private: details::shared_ptr statement_; statements_type& statements_; + view_traits_calls tc_; bool can_load_; bool use_copy_; typename view_traits::image_type* image_copy_; diff --git a/odb/mssql/view-result.txx b/odb/mssql/view-result.txx index 4842c68..641f57e 100644 --- a/odb/mssql/view-result.txx +++ b/odb/mssql/view-result.txx @@ -47,10 +47,12 @@ namespace odb view_result_impl:: view_result_impl (const query_base&, details::shared_ptr statement, - statements_type& statements) + statements_type& statements, + const schema_version_migration* svm) : base_type (statements.connection ()), statement_ (statement), statements_ (statements), + tc_ (svm), use_copy_ (false), image_copy_ (0) { @@ -65,9 +67,9 @@ namespace odb view_traits::callback (this->db_, view, callback_event::pre_load); - view_traits::init (view, - use_copy_ ? *image_copy_ : statements_.image (), - &this->db_); + tc_.init (view, + use_copy_ ? *image_copy_ : statements_.image (), + &this->db_); // If we are using a copy, make sure the callback information for // long data also comes from the copy. @@ -100,7 +102,7 @@ namespace odb if (im.version != statements_.image_version ()) { binding& b (statements_.image_binding ()); - view_traits::bind (b.bind, im); + tc_.bind (b.bind, im); statements_.image_version (im.version); b.version++; } -- cgit v1.1