From 0ed9ceef3abbd9f4ff786f6a02fe8767780e8c2c Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 10 Sep 2013 14:10:45 +0200 Subject: Schema versioning support --- odb/mysql/container-statements.hxx | 21 ++- odb/mysql/container-statements.txx | 5 +- odb/mysql/database.cxx | 8 +- odb/mysql/forward.hxx | 3 +- odb/mysql/no-id-object-result.hxx | 6 +- odb/mysql/no-id-object-result.txx | 13 +- odb/mysql/no-id-object-statements.hxx | 1 + odb/mysql/polymorphic-object-result.hxx | 6 +- odb/mysql/polymorphic-object-result.txx | 29 +-- odb/mysql/polymorphic-object-statements.hxx | 23 ++- odb/mysql/polymorphic-object-statements.txx | 16 +- odb/mysql/query.cxx | 16 +- odb/mysql/section-statements.hxx | 3 + odb/mysql/simple-object-result.hxx | 6 +- odb/mysql/simple-object-result.txx | 17 +- odb/mysql/simple-object-statements.hxx | 21 ++- odb/mysql/simple-object-statements.txx | 19 +- odb/mysql/statement-cache.hxx | 5 +- odb/mysql/statement-cache.txx | 15 ++ odb/mysql/statement.cxx | 272 ++++++++++++++++++++++++---- odb/mysql/statement.hxx | 56 +++++- odb/mysql/traits-calls.hxx | 211 +++++++++++++++++++++ odb/mysql/view-result.hxx | 6 +- odb/mysql/view-result.txx | 12 +- 24 files changed, 681 insertions(+), 109 deletions(-) create mode 100644 odb/mysql/traits-calls.hxx diff --git a/odb/mysql/container-statements.hxx b/odb/mysql/container-statements.hxx index 5d089d7..be86cda 100644 --- a/odb/mysql/container-statements.hxx +++ b/odb/mysql/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& @@ -115,7 +124,11 @@ namespace odb if (insert_ == 0) insert_.reset ( new (details::shared) insert_statement_type ( - conn_, insert_text_, insert_image_binding_, false)); + conn_, + insert_text_, + versioned_, // Process if versioned. + insert_image_binding_, + false)); return *insert_; } @@ -128,6 +141,8 @@ namespace odb new (details::shared) select_statement_type ( conn_, select_text_, + versioned_, // Process if versioned. + false, // Don't optimize. id_binding_, select_image_binding_, false)); @@ -169,6 +184,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_; @@ -273,6 +291,7 @@ namespace odb new (details::shared) update_statement_type ( this->conn_, update_text_, + this->versioned_, // Process if versioned. update_image_binding_, false)); diff --git a/odb/mysql/container-statements.txx b/odb/mysql/container-statements.txx index 2458f4d..922ea43 100644 --- a/odb/mysql/container-statements.txx +++ b/odb/mysql/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; @@ -80,6 +81,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/mysql/database.cxx b/odb/mysql/database.cxx index 9dac33b..d4ab6c4 100644 --- a/odb/mysql/database.cxx +++ b/odb/mysql/database.cxx @@ -310,7 +310,13 @@ namespace odb 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/mysql/forward.hxx b/odb/mysql/forward.hxx index 52ca8d2..88043b2 100644 --- a/odb/mysql/forward.hxx +++ b/odb/mysql/forward.hxx @@ -43,7 +43,8 @@ namespace odb { statement_select, statement_insert, - statement_update + statement_update, + statement_delete }; class binding; diff --git a/odb/mysql/no-id-object-result.hxx b/odb/mysql/no-id-object-result.hxx index aae0e47..2a1f749 100644 --- a/odb/mysql/no-id-object-result.hxx +++ b/odb/mysql/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&); @@ -66,6 +69,7 @@ namespace odb private: details::shared_ptr statement_; statements_type& statements_; + object_traits_calls tc_; std::size_t count_; }; } diff --git a/odb/mysql/no-id-object-result.txx b/odb/mysql/no-id-object-result.txx index 84c3a3d..fd040e3 100644 --- a/odb/mysql/no-id-object-result.txx +++ b/odb/mysql/no-id-object-result.txx @@ -36,10 +36,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), count_ (0) { } @@ -52,7 +54,7 @@ namespace odb fetch (); object_traits::callback (this->db_, obj, callback_event::pre_load); - object_traits::init (obj, statements_.image (), &this->db_); + tc_.init (obj, statements_.image (), &this->db_); object_traits::callback (this->db_, obj, callback_event::post_load); } @@ -95,7 +97,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++; } @@ -116,14 +118,13 @@ namespace odb typename object_traits::image_type& im (statements_.image ()); - if (object_traits::grow ( - im, statements_.select_image_truncated ())) + if (tc_.grow (im, statements_.select_image_truncated ())) im.version++; 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++; statement_->refetch (); diff --git a/odb/mysql/no-id-object-statements.hxx b/odb/mysql/no-id-object-statements.hxx index 253a3a9..0da7368 100644 --- a/odb/mysql/no-id-object-statements.hxx +++ b/odb/mysql/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)); diff --git a/odb/mysql/polymorphic-object-result.hxx b/odb/mysql/polymorphic-object-result.hxx index b8c35d0..7d108e6 100644 --- a/odb/mysql/polymorphic-object-result.hxx +++ b/odb/mysql/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 { @@ -47,7 +49,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); @@ -79,6 +82,7 @@ namespace odb private: details::shared_ptr statement_; statements_type& statements_; + object_traits_calls tc_; std::size_t count_; }; } diff --git a/odb/mysql/polymorphic-object-result.txx b/odb/mysql/polymorphic-object-result.txx index b84ac36..8072b37 100644 --- a/odb/mysql/polymorphic-object-result.txx +++ b/odb/mysql/polymorphic-object-result.txx @@ -38,10 +38,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), count_ (0) { } @@ -128,7 +130,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_); // Initialize the id image and binding and load the rest of the object // (containers, dynamic part, etc). @@ -144,7 +146,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. @@ -155,7 +157,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; @@ -235,14 +237,16 @@ namespace odb typedef object_traits_impl traits; static bool - 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 ()); return true; @@ -260,14 +264,16 @@ namespace odb typedef object_traits_impl traits; static bool - 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++; return true; @@ -287,7 +293,7 @@ namespace odb // to fetch() as a result of other statements execution. // if (statement_->cached ()) - image_rebind::rebind (statements_); + image_rebind::rebind (statements_, tc_.version ()); while (!this->end_ && (!next || count_ > statement_->fetched ())) { @@ -304,11 +310,10 @@ namespace odb typename object_traits::image_type& im (statements_.image ()); - if (object_traits::grow (im, - statements_.select_image_truncated ())) + if (tc_.grow (im, statements_.select_image_truncated ())) im.version++; - if (image_rebind::rebind (statements_)) + if (image_rebind::rebind (statements_, tc_.version ())) statement_->refetch (); // Fall throught. diff --git a/odb/mysql/polymorphic-object-statements.hxx b/odb/mysql/polymorphic-object-statements.hxx index 71b3d93..63662df 100644 --- a/odb/mysql/polymorphic-object-statements.hxx +++ b/odb/mysql/polymorphic-object-statements.hxx @@ -104,6 +104,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)); @@ -117,6 +119,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; @@ -190,7 +204,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. @@ -295,6 +312,7 @@ namespace odb new (details::shared) insert_statement_type ( conn_, object_traits::persist_statement, + object_traits::versioned, // Process if versioned. insert_image_binding_, false)); @@ -312,6 +330,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)); @@ -327,6 +347,7 @@ namespace odb new (details::shared) update_statement_type ( conn_, object_traits::update_statement, + object_traits::versioned, // Process if versioned. update_image_binding_, false)); diff --git a/odb/mysql/polymorphic-object-statements.txx b/odb/mysql/polymorphic-object-statements.txx index d8a7a1d..a228b72 100644 --- a/odb/mysql/polymorphic-object-statements.txx +++ b/odb/mysql/polymorphic-object-statements.txx @@ -10,6 +10,7 @@ #include #include #include +#include namespace odb { @@ -111,7 +112,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 ( @@ -122,14 +126,16 @@ 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 (); object_traits::callback (db, obj, callback_event::pre_load); - object_traits::init (obj, sts.image (), &db); - object_traits::load_ (sts, obj); // Load containers, etc. + tc.init (obj, sts.image (), &db); + 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/mysql/query.cxx b/odb/mysql/query.cxx index f925e76..c81f80c 100644 --- a/odb/mysql/query.cxx +++ b/odb/mysql/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 += ' '; r += i->bool_part ? "TRUE" : "FALSE"; diff --git a/odb/mysql/section-statements.hxx b/odb/mysql/section-statements.hxx index 38cf88d..2e01df4 100644 --- a/odb/mysql/section-statements.hxx +++ b/odb/mysql/section-statements.hxx @@ -100,6 +100,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)); @@ -115,6 +117,7 @@ namespace odb new (details::shared) update_statement_type ( conn_, traits::update_statement, + traits::versioned, // Process if versioned. update_image_binding_, false)); diff --git a/odb/mysql/simple-object-result.hxx b/odb/mysql/simple-object-result.hxx index edbed4c..85eb4ff 100644 --- a/odb/mysql/simple-object-result.hxx +++ b/odb/mysql/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); @@ -70,6 +73,7 @@ namespace odb private: details::shared_ptr statement_; statements_type& statements_; + object_traits_calls tc_; std::size_t count_; }; } diff --git a/odb/mysql/simple-object-result.txx b/odb/mysql/simple-object-result.txx index f1b9bfa..f444688 100644 --- a/odb/mysql/simple-object-result.txx +++ b/odb/mysql/simple-object-result.txx @@ -38,10 +38,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), count_ (0) { } @@ -68,7 +70,7 @@ namespace odb object_traits::callback (this->db_, obj, callback_event::pre_load); typename object_traits::image_type& i (statements_.image ()); - object_traits::init (obj, i, &this->db_); + tc_.init (obj, i, &this->db_); // Initialize the id image and binding and load the rest of the object // (containers, etc). @@ -84,8 +86,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); } @@ -147,7 +149,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++; } @@ -168,14 +170,13 @@ namespace odb typename object_traits::image_type& im (statements_.image ()); - if (object_traits::grow ( - im, statements_.select_image_truncated ())) + if (tc_.grow (im, statements_.select_image_truncated ())) im.version++; 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++; statement_->refetch (); diff --git a/odb/mysql/simple-object-statements.hxx b/odb/mysql/simple-object-statements.hxx index 73dc660..1ce8472 100644 --- a/odb/mysql/simple-object-statements.hxx +++ b/odb/mysql/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_, false)); @@ -363,6 +366,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)); @@ -378,6 +383,7 @@ namespace odb new (details::shared) update_statement_type ( conn_, object_traits::update_statement, + object_traits::versioned, // Process if versioned. update_image_binding_, false)); @@ -454,14 +460,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/mysql/simple-object-statements.txx b/odb/mysql/simple-object-statements.txx index 082d9db..506b7a3 100644 --- a/odb/mysql/simple-object-statements.txx +++ b/odb/mysql/simple-object-statements.txx @@ -8,6 +8,7 @@ #include #include +#include namespace odb { @@ -70,8 +71,9 @@ namespace odb } template + template void object_statements:: - load_delayed_ () + load_delayed_ (const schema_version_migration* svm) { database& db (connection ().database ()); @@ -86,7 +88,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 (); object_traits::callback (db, *l.obj, callback_event::pre_load); @@ -95,11 +99,14 @@ 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); - object_traits::load_ (*this, *l.obj); // Load containers, etc. + tc.init (*l.obj, image (), &db); + + // 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 @@ -115,7 +122,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/mysql/statement-cache.hxx b/odb/mysql/statement-cache.hxx index 9c8cf52..7f41432 100644 --- a/odb/mysql/statement-cache.hxx +++ b/odb/mysql/statement-cache.hxx @@ -29,7 +29,9 @@ namespace odb class LIBODB_MYSQL_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& @@ -46,6 +48,7 @@ namespace odb details::type_info_comparator> map; connection& conn_; + unsigned int version_seq_; map map_; }; } diff --git a/odb/mysql/statement-cache.txx b/odb/mysql/statement-cache.txx index 0ac74a4..b110d97 100644 --- a/odb/mysql/statement-cache.txx +++ b/odb/mysql/statement-cache.txx @@ -2,6 +2,8 @@ // copyright : Copyright (c) 2005-2013 Code Synthesis Tools CC // license : GNU GPL v2; see accompanying LICENSE file +#include + namespace odb { namespace mysql @@ -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/mysql/statement.cxx b/odb/mysql/statement.cxx index 2a7a011..cf03a9b 100644 --- a/odb/mysql/statement.cxx +++ b/odb/mysql/statement.cxx @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2005-2013 Code Synthesis Tools CC // license : GNU GPL v2; see accompanying LICENSE file -#include // std::strlen +#include // std::strlen, std::memmove, std::memset #include #include @@ -23,19 +23,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 (); @@ -43,16 +60,55 @@ namespace odb } else { - text_ = text; + text_ = text; // Potentially temporary, see init(). n = strlen (text_); } - 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->buffer, proc->count, sizeof (MYSQL_BIND), + '`', '`', + optimize, + text_copy_); + break; + case statement_insert: + process_insert (text_, + &proc->bind->buffer, proc->count, sizeof (MYSQL_BIND), + '?', + text_copy_); + break; + case statement_update: + process_update (text_, + &proc->bind->buffer, proc->count, sizeof (MYSQL_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; + stmt_.reset (conn_.alloc_stmt_handle ()); conn_.clear (); @@ -69,6 +125,61 @@ namespace odb } } + size_t statement:: + process_bind (MYSQL_BIND* b, size_t n) + { + size_t shifts (0); + for (MYSQL_BIND* e (b + n); b != e;) + { + if (b->buffer == 0) + { + // It is possible that this array has already been processed + // (shared among multiple statements). + // + if (b->length != 0) + { + n -= e - b; + break; + } + + e--; + + // Shift the rest of the entries to the left. + // + memmove (b, b + 1, (e - b) * sizeof (MYSQL_BIND)); + + // Store the original position of the NULL entry at the end. + // + e->buffer = 0; + e->length = reinterpret_cast (b + shifts); + + shifts++; + continue; + } + + b++; + } + + return n - shifts; + } + + void statement:: + restore_bind (MYSQL_BIND* b, size_t n) + { + for (MYSQL_BIND* e (b + n - 1); e->buffer == 0 && e->length != 0;) + { + MYSQL_BIND* p (reinterpret_cast (e->length)); + + // Shift the entries from the specified position to the right. + // + memmove (p + 1, p, (e - p) * sizeof (MYSQL_BIND)); + + // Restore the original NULL entry. + // + memset (p, 0, sizeof (MYSQL_BIND)); + } + } + statement:: ~statement () { @@ -109,10 +220,14 @@ 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), + : statement (conn, + text, statement_select, + (process ? &result : 0), optimize), end_ (false), cached_ (false), freed_ (true), @@ -126,11 +241,16 @@ namespace odb 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), + bool copy_text) + : statement (conn, + text, statement_select, + (process ? &result : 0), optimize, + copy_text), end_ (false), cached_ (false), freed_ (true), @@ -143,8 +263,14 @@ namespace odb } select_statement:: - select_statement (connection_type& conn, const string& t, binding& result) - : statement (conn, t), + select_statement (connection_type& conn, + const string& text, + bool process, + bool optimize, + binding& result) + : statement (conn, + text, statement_select, + (process ? &result : 0), optimize), end_ (false), cached_ (false), freed_ (true), @@ -157,10 +283,15 @@ namespace odb 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), + bool copy_text) + : statement (conn, + text, statement_select, + (process ? &result : 0), optimize, + copy_text), end_ (false), cached_ (false), freed_ (true), @@ -186,6 +317,8 @@ namespace odb if (param_ != 0 && param_version_ != param_->version) { + // For now cannot have NULL entries. + // if (mysql_stmt_bind_param (stmt_, param_->bind)) translate_error (conn_, stmt_); @@ -234,16 +367,21 @@ namespace odb { if (result_version_ != result_.version) { + size_t count (process_bind (result_.bind, result_.count)); + // 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 (mysql_stmt_field_count (stmt_) == result_.count); + assert (mysql_stmt_field_count (stmt_) == count); if (mysql_stmt_bind_result (stmt_, result_.bind)) translate_error (conn_, stmt_); + if (count != result_.count) + restore_bind (result_.bind, result_.count); + result_version_ = result_.version; } @@ -287,16 +425,23 @@ namespace odb { // Re-fetch columns that were truncated. // + unsigned int col (0); for (size_t i (0); i < result_.count; ++i) { - if (*result_.bind[i].error) + MYSQL_BIND& b (result_.bind[i]); + + if (b.buffer == 0) // Skip NULL entries. + continue; + + if (*b.error) { - *result_.bind[i].error = 0; + *b.error = 0; - if (mysql_stmt_fetch_column ( - stmt_, result_.bind + i, static_cast (i), 0)) + if (mysql_stmt_fetch_column (stmt_, &b, col, 0)) translate_error (conn_, stmt_); } + + col++; } } @@ -338,17 +483,30 @@ namespace odb } insert_statement:: - insert_statement (connection_type& conn, const string& t, binding& param) - : statement (conn, t), param_ (param), param_version_ (0) + insert_statement (connection_type& conn, + const string& text, + bool process, + binding& param) + : statement (conn, + text, statement_insert, + (process ? ¶m : 0), false), + param_ (param), + param_version_ (0) { } insert_statement:: insert_statement (connection_type& conn, - const char* t, + const char* text, + bool process, binding& param, - bool ct) - : statement (conn, t, ct), param_ (param), param_version_ (0) + bool copy_text) + : statement (conn, + text, statement_insert, + (process ? ¶m : 0), false, + copy_text), + param_ (param), + param_version_ (0) { } @@ -362,9 +520,14 @@ namespace odb if (param_version_ != param_.version) { + size_t count (process_bind (param_.bind, param_.count)); + if (mysql_stmt_bind_param (stmt_, param_.bind)) translate_error (conn_, stmt_); + if (count != param_.count) + restore_bind (param_.bind, param_.count); + param_version_ = param_.version; } @@ -402,17 +565,30 @@ namespace odb } update_statement:: - update_statement (connection_type& conn, const string& t, binding& param) - : statement (conn, t), param_ (param), param_version_ (0) + update_statement (connection_type& conn, + const string& text, + bool process, + binding& param) + : statement (conn, + text, statement_update, + (process ? ¶m : 0), false), + param_ (param), + param_version_ (0) { } update_statement:: update_statement (connection_type& conn, - const char* t, + const char* text, + bool process, binding& param, - bool ct) - : statement (conn, t, ct), param_ (param), param_version_ (0) + bool copy_text) + : statement (conn, + text, statement_update, + (process ? ¶m : 0), false, + copy_text), + param_ (param), + param_version_ (0) { } @@ -426,9 +602,14 @@ namespace odb if (param_version_ != param_.version) { + size_t count (process_bind (param_.bind, param_.count)); + if (mysql_stmt_bind_param (stmt_, param_.bind)) translate_error (conn_, stmt_); + if (count != param_.count) + restore_bind (param_.bind, param_.count); + param_version_ = param_.version; } @@ -460,17 +641,28 @@ namespace odb } delete_statement:: - delete_statement (connection_type& conn, const string& t, binding& param) - : statement (conn, t), param_ (param), param_version_ (0) + delete_statement (connection_type& conn, + const string& text, + binding& param) + : statement (conn, + text, statement_delete, + 0, false), + param_ (param), + param_version_ (0) { } delete_statement:: delete_statement (connection_type& conn, - const char* t, + const char* text, binding& param, - bool ct) - : statement (conn, t, ct), param_ (param), param_version_ (0) + bool copy_text) + : statement (conn, + text, statement_delete, + 0, false, + copy_text), + param_ (param), + param_version_ (0) { } @@ -484,6 +676,8 @@ namespace odb if (param_version_ != param_.version) { + // Cannot have NULL entries for now. + // if (mysql_stmt_bind_param (stmt_, param_.bind)) translate_error (conn_, stmt_); diff --git a/odb/mysql/statement.hxx b/odb/mysql/statement.hxx index 0c4f22b..5fa53ef 100644 --- a/odb/mysql/statement.hxx +++ b/odb/mysql/statement.hxx @@ -50,6 +50,17 @@ 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; + } + // Cancel the statement execution (e.g., result fetching) so // that another statement can be executed on the connection. // @@ -57,12 +68,39 @@ namespace odb cancel (); 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); + + // Process the bind array so that all non-NULL entries are at + // the beginning. Return the actual number of bound columns. + // + static std::size_t + process_bind (MYSQL_BIND*, std::size_t n); + + // Restore the original locations of the NULL entries in the bind + // array. + // + static void + restore_bind (MYSQL_BIND*, std::size_t n); private: void - init (std::size_t text_size); + init (std::size_t text_size, + statement_kind, + const binding* process, + bool optimize); protected: connection_type& conn_; @@ -79,21 +117,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); @@ -187,10 +233,12 @@ namespace odb insert_statement (connection_type& conn, const std::string& text, + bool process_text, binding& param); insert_statement (connection_type& conn, const char* text, + bool process_text, binding& param, bool copy_text = true); @@ -220,10 +268,12 @@ namespace odb update_statement (connection_type& conn, const std::string& text, + bool process_text, binding& param); update_statement (connection_type& conn, const char* text, + bool process_text, binding& param, bool copy_text = true); diff --git a/odb/mysql/traits-calls.hxx b/odb/mysql/traits-calls.hxx new file mode 100644 index 0000000..41a0a97 --- /dev/null +++ b/odb/mysql/traits-calls.hxx @@ -0,0 +1,211 @@ +// file : odb/mysql/traits-calls.hxx +// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_MYSQL_TRAITS_CALLS_HXX +#define ODB_MYSQL_TRAITS_CALLS_HXX + +#include + +#include // std::size_t + +#include +#include +#include + +#include +#include + +namespace odb +{ + namespace mysql + { + // + // 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; + + object_traits_calls (const schema_version_migration*) {} + + const schema_version_migration* + version () const {return 0;} + + static bool + grow (image_type& i, my_bool* t) + { + return traits::grow (i, t); + } + + static void + bind (MYSQL_BIND* b, image_type& i, statement_kind sk) + { + traits::bind (b, i, sk); + } + + // Poly-derived version. + // + static void + bind (MYSQL_BIND* b, + const MYSQL_BIND* 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; + + object_traits_calls (const schema_version_migration* svm): svm_ (*svm) {} + + const schema_version_migration* + version () const {return &svm_;} + + bool + grow (image_type& i, my_bool* t) const + { + return traits::grow (i, t, svm_); + } + + void + bind (MYSQL_BIND* b, image_type& i, statement_kind sk) const + { + traits::bind (b, i, sk, svm_); + } + + // Poly-derived version. + // + void + bind (MYSQL_BIND* b, + const MYSQL_BIND* 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; + + view_traits_calls (const schema_version_migration*) {} + + static bool + grow (image_type& i, my_bool* t) + { + return traits::grow (i, t); + } + + static void + bind (MYSQL_BIND* 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; + + view_traits_calls (const schema_version_migration* svm): svm_ (*svm) {} + + bool + grow (image_type& i, my_bool* t) const + { + return traits::grow (i, t, svm_); + } + + void + bind (MYSQL_BIND* 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_MYSQL_TRAITS_CALLS_HXX diff --git a/odb/mysql/view-result.hxx b/odb/mysql/view-result.hxx index 8fa9d7f..8f4d3fd 100644 --- a/odb/mysql/view-result.hxx +++ b/odb/mysql/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&); @@ -66,6 +69,7 @@ namespace odb private: details::shared_ptr statement_; statements_type& statements_; + view_traits_calls tc_; std::size_t count_; }; } diff --git a/odb/mysql/view-result.txx b/odb/mysql/view-result.txx index 895f3fe..5d4c2e5 100644 --- a/odb/mysql/view-result.txx +++ b/odb/mysql/view-result.txx @@ -36,10 +36,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), count_ (0) { } @@ -52,7 +54,7 @@ namespace odb fetch (); view_traits::callback (this->db_, view, callback_event::pre_load); - view_traits::init (view, statements_.image (), &this->db_); + tc_.init (view, statements_.image (), &this->db_); view_traits::callback (this->db_, view, callback_event::post_load); } @@ -95,7 +97,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++; } @@ -116,13 +118,13 @@ namespace odb typename view_traits::image_type& im (statements_.image ()); - if (view_traits::grow (im, statements_.image_truncated ())) + if (tc_.grow (im, statements_.image_truncated ())) im.version++; 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++; statement_->refetch (); -- cgit v1.1