From 31fd2870497686667f20b2d43467a07482f8ab40 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 10 Sep 2013 14:10:45 +0200 Subject: Schema versioning support --- odb/sqlite/container-statements.hxx | 20 ++- odb/sqlite/container-statements.txx | 5 +- odb/sqlite/database.cxx | 7 +- odb/sqlite/forward.hxx | 4 +- odb/sqlite/no-id-object-result.hxx | 6 +- odb/sqlite/no-id-object-result.txx | 14 +- odb/sqlite/no-id-object-statements.hxx | 1 + odb/sqlite/polymorphic-object-result.hxx | 6 +- odb/sqlite/polymorphic-object-result.txx | 30 ++-- odb/sqlite/polymorphic-object-statements.hxx | 23 ++- odb/sqlite/polymorphic-object-statements.txx | 16 +- odb/sqlite/query.cxx | 16 +- odb/sqlite/section-statements.hxx | 3 + odb/sqlite/simple-object-result.hxx | 6 +- odb/sqlite/simple-object-result.txx | 18 ++- odb/sqlite/simple-object-statements.hxx | 21 ++- odb/sqlite/simple-object-statements.txx | 19 ++- odb/sqlite/statement-cache.cxx | 1 + odb/sqlite/statement-cache.hxx | 1 + odb/sqlite/statement-cache.txx | 15 ++ odb/sqlite/statement.cxx | 206 +++++++++++++++++++------ odb/sqlite/statement.hxx | 54 ++++++- odb/sqlite/traits-calls.hxx | 215 +++++++++++++++++++++++++++ odb/sqlite/view-result.hxx | 6 +- odb/sqlite/view-result.txx | 14 +- 25 files changed, 607 insertions(+), 120 deletions(-) create mode 100644 odb/sqlite/traits-calls.hxx diff --git a/odb/sqlite/container-statements.hxx b/odb/sqlite/container-statements.hxx index e9c2d3b..058a255 100644 --- a/odb/sqlite/container-statements.hxx +++ b/odb/sqlite/container-statements.hxx @@ -10,6 +10,7 @@ #include // std::size_t #include +#include #include #include @@ -57,6 +58,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& @@ -114,7 +123,10 @@ namespace odb if (insert_ == 0) insert_.reset ( new (details::shared) insert_statement_type ( - conn_, insert_text_, insert_image_binding_)); + conn_, + insert_text_, + versioned_, // Process if versioned. + insert_image_binding_)); return *insert_; } @@ -127,6 +139,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_)); @@ -167,6 +181,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_; @@ -270,6 +287,7 @@ namespace odb new (details::shared) update_statement_type ( this->conn_, update_text_, + this->versioned_, // Process if versioned. update_image_binding_)); return *update_; diff --git a/odb/sqlite/container-statements.txx b/odb/sqlite/container-statements.txx index 5024b5b..6458be8 100644 --- a/odb/sqlite/container-statements.txx +++ b/odb/sqlite/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/sqlite/database.cxx b/odb/sqlite/database.cxx index d5399f7..bed0b32 100644 --- a/odb/sqlite/database.cxx +++ b/odb/sqlite/database.cxx @@ -230,7 +230,12 @@ namespace odb try { - select_statement st (c, text, param, result); + select_statement st (c, + text, + false, // Don't process. + false, // Don't optimize. + param, + result); st.execute (); auto_result ar (st); diff --git a/odb/sqlite/forward.hxx b/odb/sqlite/forward.hxx index 456ff43..8904011 100644 --- a/odb/sqlite/forward.hxx +++ b/odb/sqlite/forward.hxx @@ -43,7 +43,9 @@ namespace odb { statement_select, statement_insert, - statement_update + statement_update, + statement_delete, + statement_generic }; class binding; diff --git a/odb/sqlite/no-id-object-result.hxx b/odb/sqlite/no-id-object-result.hxx index 236bf82..49576c3 100644 --- a/odb/sqlite/no-id-object-result.hxx +++ b/odb/sqlite/no-id-object-result.hxx @@ -9,6 +9,7 @@ #include // std::size_t +#include #include #include @@ -16,6 +17,7 @@ #include #include // query_base, query_params #include +#include namespace odb { @@ -40,7 +42,8 @@ namespace odb no_id_object_result_impl (const query_base&, const details::shared_ptr&, - statements_type&); + statements_type&, + const schema_version_migration*); virtual void load (object_type&); @@ -66,6 +69,7 @@ namespace odb details::shared_ptr params_; details::shared_ptr statement_; statements_type& statements_; + object_traits_calls tc_; }; } } diff --git a/odb/sqlite/no-id-object-result.txx b/odb/sqlite/no-id-object-result.txx index e857820..cf0fc15 100644 --- a/odb/sqlite/no-id-object-result.txx +++ b/odb/sqlite/no-id-object-result.txx @@ -37,11 +37,13 @@ namespace odb no_id_object_result_impl:: no_id_object_result_impl (const query_base& q, const details::shared_ptr& s, - statements_type& sts) + statements_type& sts, + const schema_version_migration* svm) : base_type (sts.connection ()), params_ (q.parameters ()), statement_ (s), - statements_ (sts) + statements_ (sts), + tc_ (svm) { } @@ -57,7 +59,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++; } @@ -66,13 +68,13 @@ namespace odb if (r == select_statement::truncated) { - 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_->reload (); @@ -80,7 +82,7 @@ namespace odb } object_traits::callback (this->db_, obj, callback_event::pre_load); - object_traits::init (obj, im, &this->db_); + tc_.init (obj, im, &this->db_); object_traits::callback (this->db_, obj, callback_event::post_load); } diff --git a/odb/sqlite/no-id-object-statements.hxx b/odb/sqlite/no-id-object-statements.hxx index 6939fc7..2e0cb05 100644 --- a/odb/sqlite/no-id-object-statements.hxx +++ b/odb/sqlite/no-id-object-statements.hxx @@ -87,6 +87,7 @@ namespace odb new (details::shared) insert_statement_type ( conn_, object_traits::persist_statement, + object_traits::versioned, // Process if versioned. insert_image_binding_)); } diff --git a/odb/sqlite/polymorphic-object-result.hxx b/odb/sqlite/polymorphic-object-result.hxx index 493bced..7192a26 100644 --- a/odb/sqlite/polymorphic-object-result.hxx +++ b/odb/sqlite/polymorphic-object-result.hxx @@ -9,6 +9,7 @@ #include // std::size_t +#include #include #include @@ -16,6 +17,7 @@ #include #include // query_base, query_params #include +#include namespace odb { @@ -48,7 +50,8 @@ namespace odb polymorphic_object_result_impl ( const query_base&, const details::shared_ptr&, - statements_type&); + statements_type&, + const schema_version_migration*); virtual void load (object_type*, bool fetch); @@ -84,6 +87,7 @@ namespace odb details::shared_ptr params_; details::shared_ptr statement_; statements_type& statements_; + object_traits_calls tc_; }; } } diff --git a/odb/sqlite/polymorphic-object-result.txx b/odb/sqlite/polymorphic-object-result.txx index 9c79414..6d761b5 100644 --- a/odb/sqlite/polymorphic-object-result.txx +++ b/odb/sqlite/polymorphic-object-result.txx @@ -40,11 +40,13 @@ namespace odb polymorphic_object_result_impl ( const query_base& q, const details::shared_ptr& s, - statements_type& sts) + statements_type& sts, + const schema_version_migration* svm) : base_type (sts.connection ()), params_ (q.parameters ()), statement_ (s), - statements_ (sts) + statements_ (sts), + tc_ (svm) { } @@ -123,7 +125,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). @@ -139,7 +141,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. @@ -150,7 +152,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; @@ -199,14 +201,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; @@ -224,14 +228,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; @@ -250,7 +256,7 @@ namespace odb // The image can grow between calls to load() as a result of other // statements execution. // - image_rebind::rebind (statements_); + image_rebind::rebind (statements_, tc_.version ()); select_statement::result r (statement_->load ()); @@ -258,10 +264,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_->reload (); } } diff --git a/odb/sqlite/polymorphic-object-statements.hxx b/odb/sqlite/polymorphic-object-statements.hxx index fa5f6af..2586cb0 100644 --- a/odb/sqlite/polymorphic-object-statements.hxx +++ b/odb/sqlite/polymorphic-object-statements.hxx @@ -105,6 +105,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_)); } @@ -118,6 +120,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; @@ -191,7 +205,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. @@ -297,6 +314,7 @@ namespace odb new (details::shared) insert_statement_type ( conn_, object_traits::persist_statement, + object_traits::versioned, // Process if versioned. insert_image_binding_)); } @@ -315,6 +333,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])); } @@ -331,6 +351,7 @@ namespace odb new (details::shared) update_statement_type ( conn_, object_traits::update_statement, + object_traits::versioned, // Process if versioned. update_image_binding_)); } diff --git a/odb/sqlite/polymorphic-object-statements.txx b/odb/sqlite/polymorphic-object-statements.txx index 999eb3e..b97cd14 100644 --- a/odb/sqlite/polymorphic-object-statements.txx +++ b/odb/sqlite/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/sqlite/query.cxx b/odb/sqlite/query.cxx index 120be98..fc1a046 100644 --- a/odb/sqlite/query.cxx +++ b/odb/sqlite/query.cxx @@ -159,8 +159,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; @@ -213,7 +213,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; } @@ -272,7 +272,7 @@ namespace odb { case clause_part::kind_column: { - if (last != ' ' && last != '(') + if (last != ' ' && last != '\n' && last != '(') r += ' '; r += i->part; @@ -280,7 +280,7 @@ namespace odb } case clause_part::kind_param: { - if (last != ' ' && last != '(') + if (last != ' ' && last != '\n' && last != '(') r += ' '; // Add the conversion expression, if any. @@ -307,8 +307,8 @@ namespace odb const string& p (i->part); char first (!p.empty () ? p[0] : ' '); - if (last != ' ' && last != '(' && - first != ' ' && first != ',' && first != ')') + if (last != ' ' && first != '\n' && last != '(' && + first != ' ' && last != '\n' && first != ',' && first != ')') r += ' '; r += p; @@ -316,7 +316,7 @@ namespace odb } case clause_part::kind_bool: { - if (last != ' ' && last != '(') + if (last != ' ' && last != '\n' && last != '(') r += ' '; r += i->bool_part ? "1" : "0"; diff --git a/odb/sqlite/section-statements.hxx b/odb/sqlite/section-statements.hxx index 3f0d9c4..8bdbda1 100644 --- a/odb/sqlite/section-statements.hxx +++ b/odb/sqlite/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_)); @@ -114,6 +116,7 @@ namespace odb new (details::shared) update_statement_type ( conn_, traits::update_statement, + traits::versioned, // Process if versioned. update_image_binding_)); return *update_; diff --git a/odb/sqlite/simple-object-result.hxx b/odb/sqlite/simple-object-result.hxx index 9dd258a..3888a56 100644 --- a/odb/sqlite/simple-object-result.hxx +++ b/odb/sqlite/simple-object-result.hxx @@ -9,6 +9,7 @@ #include // std::size_t +#include #include #include @@ -16,6 +17,7 @@ #include #include // query_base, query_params #include +#include namespace odb { @@ -41,7 +43,8 @@ namespace odb object_result_impl (const query_base&, const details::shared_ptr&, - statements_type&); + statements_type&, + const schema_version_migration*); virtual void load (object_type&, bool fetch); @@ -74,6 +77,7 @@ namespace odb details::shared_ptr params_; details::shared_ptr statement_; statements_type& statements_; + object_traits_calls tc_; }; } } diff --git a/odb/sqlite/simple-object-result.txx b/odb/sqlite/simple-object-result.txx index fe31300..2c4340d 100644 --- a/odb/sqlite/simple-object-result.txx +++ b/odb/sqlite/simple-object-result.txx @@ -39,11 +39,13 @@ namespace odb object_result_impl:: object_result_impl (const query_base& q, const details::shared_ptr& s, - statements_type& sts) + statements_type& sts, + const schema_version_migration* svm) : base_type (sts.connection ()), params_ (q.parameters ()), statement_ (s), - statements_ (sts) + statements_ (sts), + tc_ (svm) { } @@ -62,7 +64,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). @@ -78,8 +80,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); } @@ -118,7 +120,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++; } @@ -127,13 +129,13 @@ namespace odb if (r == select_statement::truncated) { - 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_->reload (); diff --git a/odb/sqlite/simple-object-statements.hxx b/odb/sqlite/simple-object-statements.hxx index a1e7678..f438d90 100644 --- a/odb/sqlite/simple-object-statements.hxx +++ b/odb/sqlite/simple-object-statements.hxx @@ -244,8 +244,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, @@ -257,12 +259,12 @@ namespace odb } void - load_delayed () + load_delayed (const schema_version_migration* svm) { assert (locked ()); if (!delayed_.empty ()) - load_delayed_ (); + load_delayed_ (svm); } void @@ -351,6 +353,7 @@ namespace odb new (details::shared) insert_statement_type ( conn_, object_traits::persist_statement, + object_traits::versioned, // Process if versioned. insert_image_binding_)); } @@ -366,6 +369,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_)); } @@ -382,6 +387,7 @@ namespace odb new (details::shared) update_statement_type ( conn_, object_traits::update_statement, + object_traits::versioned, // Process if versioned. update_image_binding_)); } @@ -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/sqlite/simple-object-statements.txx b/odb/sqlite/simple-object-statements.txx index 15689c6..552806c 100644 --- a/odb/sqlite/simple-object-statements.txx +++ b/odb/sqlite/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/sqlite/statement-cache.cxx b/odb/sqlite/statement-cache.cxx index bb34aed..e29f83e 100644 --- a/odb/sqlite/statement-cache.cxx +++ b/odb/sqlite/statement-cache.cxx @@ -13,6 +13,7 @@ namespace odb statement_cache:: statement_cache (connection& conn) : conn_ (conn), + version_seq_ (conn.database ().schema_version_sequence ()), // String lengths below include '\0', as per SQLite manual // suggestions. // diff --git a/odb/sqlite/statement-cache.hxx b/odb/sqlite/statement-cache.hxx index bd90a44..f943ffc 100644 --- a/odb/sqlite/statement-cache.hxx +++ b/odb/sqlite/statement-cache.hxx @@ -89,6 +89,7 @@ namespace odb details::type_info_comparator> map; connection& conn_; + unsigned int version_seq_; details::shared_ptr begin_; mutable details::shared_ptr begin_immediate_; diff --git a/odb/sqlite/statement-cache.txx b/odb/sqlite/statement-cache.txx index 4c6e3cd..8d8e358 100644 --- a/odb/sqlite/statement-cache.txx +++ b/odb/sqlite/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 sqlite @@ -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/sqlite/statement.cxx b/odb/sqlite/statement.cxx index de71e12..f20d8da 100644 --- a/odb/sqlite/statement.cxx +++ b/odb/sqlite/statement.cxx @@ -24,6 +24,9 @@ namespace odb statement:: ~statement () { + if (empty ()) + return; + { odb::tracer* t; if ((t = conn_.transaction_tracer ()) || @@ -39,8 +42,50 @@ namespace odb } void statement:: - init (const char* text, std::size_t text_size) + init (const char* text, + std::size_t text_size, + statement_kind sk, + const binding* proc, + bool optimize) { + string tmp; + if (proc != 0) + { + switch (sk) + { + case statement_select: + process_select (text, + &proc->bind->buffer, proc->count, sizeof (bind), + '"', '"', + optimize, + tmp); + break; + case statement_insert: + process_insert (text, + &proc->bind->buffer, proc->count, sizeof (bind), + '?', + tmp); + break; + case statement_update: + process_update (text, + &proc->bind->buffer, proc->count, sizeof (bind), + '?', + tmp); + break; + case statement_delete: + case statement_generic: + assert (false); + } + + text = tmp.c_str (); + text_size = tmp.size (); + } + + // Empty statement. + // + if (*text == '\0') + return; + int e; sqlite3_stmt* stmt (0); while ((e = sqlite3_prepare_v2 (conn_.handle (), @@ -83,15 +128,18 @@ namespace odb // SQLite parameters are counted from 1. // - n++; - for (size_t i (1); e == SQLITE_OK && i < n; ++i, ++p) + for (size_t i (0), j (1); e == SQLITE_OK && i < n; ++i) { - const bind& b (*p); - int j (static_cast (i)); + const bind& b (p[i]); + + if (b.buffer == 0) // Skip NULL entries. + continue; + + int c (static_cast (j++)); if (b.is_null != 0 && *b.is_null) { - e = sqlite3_bind_null (stmt_, j); + e = sqlite3_bind_null (stmt_, c); continue; } @@ -100,19 +148,19 @@ namespace odb case bind::integer: { long long v (*static_cast (b.buffer)); - e = sqlite3_bind_int64 (stmt_, j, static_cast (v)); + e = sqlite3_bind_int64 (stmt_, c, static_cast (v)); break; } case bind::real: { double v (*static_cast (b.buffer)); - e = sqlite3_bind_double (stmt_, j, v); + e = sqlite3_bind_double (stmt_, c, v); break; } case bind::text: { e = sqlite3_bind_text (stmt_, - j, + c, static_cast (b.buffer), static_cast (*b.size), SQLITE_STATIC); @@ -121,7 +169,7 @@ namespace odb case bind::text16: { e = sqlite3_bind_text16 (stmt_, - j, + c, b.buffer, static_cast (*b.size), SQLITE_STATIC); @@ -130,7 +178,7 @@ namespace odb case bind::blob: { e = sqlite3_bind_blob (stmt_, - j, + c, b.buffer, static_cast (*b.size), SQLITE_STATIC); @@ -144,21 +192,20 @@ namespace odb } bool statement:: - bind_result (const bind* p, size_t n, bool truncated) + bind_result (const bind* p, size_t count, bool truncated) { - // 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 (sqlite3_data_count (stmt_)) == n); - bool r (true); + int col_count (sqlite3_data_count (stmt_)); - for (size_t i (0); i < n; ++i) + int col (0); + for (size_t i (0); i != count && col != col_count; ++i) { const bind& b (p[i]); - int j (static_cast (i)); + + if (b.buffer == 0) // Skip NULL entries. + continue; + + int c (col++); if (truncated && (b.truncated == 0 || !*b.truncated)) continue; @@ -170,7 +217,7 @@ namespace odb // if (!truncated) { - *b.is_null = sqlite3_column_type (stmt_, j) == SQLITE_NULL; + *b.is_null = sqlite3_column_type (stmt_, c) == SQLITE_NULL; if (*b.is_null) continue; @@ -181,13 +228,13 @@ namespace odb case bind::integer: { *static_cast (b.buffer) = - static_cast (sqlite3_column_int64 (stmt_, j)); + static_cast (sqlite3_column_int64 (stmt_, c)); break; } case bind::real: { *static_cast (b.buffer) = - sqlite3_column_double (stmt_, j); + sqlite3_column_double (stmt_, c); break; } case bind::text: @@ -203,15 +250,15 @@ namespace odb if (b.type != bind::text16) { d = b.type == bind::text - ? sqlite3_column_text (stmt_, j) - : sqlite3_column_blob (stmt_, j); - *b.size = static_cast (sqlite3_column_bytes (stmt_, j)); + ? sqlite3_column_text (stmt_, c) + : sqlite3_column_blob (stmt_, c); + *b.size = static_cast (sqlite3_column_bytes (stmt_, c)); } else { - d = sqlite3_column_text16 (stmt_, j); + d = sqlite3_column_text16 (stmt_, c); *b.size = static_cast ( - sqlite3_column_bytes16 (stmt_, j)); + sqlite3_column_bytes16 (stmt_, c)); } if (*b.size > b.capacity) @@ -229,6 +276,13 @@ namespace odb } } + // 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 (col == col_count); + return r; } @@ -237,14 +291,18 @@ namespace odb generic_statement:: generic_statement (connection_type& conn, const string& text) - : statement (conn, text), + : statement (conn, + text, statement_generic, + 0, false), result_set_ (stmt_ ? sqlite3_column_count (stmt_) != 0: false) { } generic_statement:: generic_statement (connection_type& conn, const char* text) - : statement (conn, text), + : statement (conn, + text, statement_generic, + 0, false), result_set_ (stmt_ ? sqlite3_column_count (stmt_) != 0: false) { } @@ -253,7 +311,9 @@ namespace odb generic_statement (connection_type& conn, const char* text, std::size_t text_size) - : statement (conn, text, text_size), + : statement (conn, + text, text_size, statement_generic, + 0, false), result_set_ (stmt_ ? sqlite3_column_count (stmt_) != 0: false) { } @@ -312,32 +372,58 @@ namespace odb select_statement:: select_statement (connection_type& conn, const string& text, + bool process, + bool optimize, binding& param, binding& result) - : statement (conn, text), param_ (¶m), result_ (result) + : statement (conn, + text, statement_select, + (process ? &result : 0), optimize), + param_ (¶m), + result_ (result) { } select_statement:: select_statement (connection_type& conn, const char* text, + bool process, + bool optimize, binding& param, binding& result) - : statement (conn, text), param_ (¶m), result_ (result) + : statement (conn, + text, statement_select, + (process ? &result : 0), optimize), + param_ (¶m), + result_ (result) { } select_statement:: select_statement (connection_type& conn, const string& text, + bool process, + bool optimize, binding& result) - : statement (conn, text), param_ (0), result_ (result) + : statement (conn, + text, statement_select, + (process ? &result : 0), optimize), + param_ (0), + result_ (result) { } select_statement:: - select_statement (connection_type& conn, const char* text, binding& result) - : statement (conn, text), param_ (0), result_ (result) + select_statement (connection_type& conn, + const char* text, + bool process, + bool optimize, + binding& result) + : statement (conn, + text, statement_select, + (process ? &result : 0), optimize), + param_ (0), + result_ (result) { } @@ -428,14 +514,24 @@ namespace odb insert_statement:: insert_statement (connection_type& conn, const string& text, + bool process, binding& param) - : statement (conn, text), param_ (param) + : statement (conn, + text, statement_insert, + (process ? ¶m : 0), false), + param_ (param) { } insert_statement:: - insert_statement (connection_type& conn, const char* text, binding& param) - : statement (conn, text), param_ (param) + insert_statement (connection_type& conn, + const char* text, + bool process, + binding& param) + : statement (conn, + text, statement_insert, + (process ? ¶m : 0), false), + param_ (param) { } @@ -499,14 +595,24 @@ namespace odb update_statement:: update_statement (connection_type& conn, const string& text, + bool process, binding& param) - : statement (conn, text), param_ (param) + : statement (conn, + text, statement_update, + (process ? ¶m : 0), false), + param_ (param) { } update_statement:: - update_statement (connection_type& conn, const char* text, binding& param) - : statement (conn, text), param_ (param) + update_statement (connection_type& conn, + const char* text, + bool process, + binding& param) + : statement (conn, + text, statement_update, + (process ? ¶m : 0), false), + param_ (param) { } @@ -554,13 +660,21 @@ namespace odb delete_statement (connection_type& conn, const string& text, binding& param) - : statement (conn, text), param_ (param) + : statement (conn, + text, statement_delete, + 0, false), + param_ (param) { } delete_statement:: - delete_statement (connection_type& conn, const char* text, binding& param) - : statement (conn, text), param_ (param) + delete_statement (connection_type& conn, + const char* text, + binding& param) + : statement (conn, + text, statement_delete, + 0, false), + param_ (param) { } diff --git a/odb/sqlite/statement.hxx b/odb/sqlite/statement.hxx index eb55e58..05fa9bd 100644 --- a/odb/sqlite/statement.hxx +++ b/odb/sqlite/statement.hxx @@ -53,25 +53,49 @@ 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& conn, const std::string& text) + // We keep two versions to take advantage of std::string COW. + // + statement (connection_type& conn, + const std::string& text, + statement_kind sk, + const binding* process, + bool optimize) : conn_ (conn) { - init (text.c_str (), text.size ()); + init (text.c_str (), text.size (), sk, process, optimize); } - statement (connection_type& conn, const char* text) + statement (connection_type& conn, + const char* text, + statement_kind sk, + const binding* process, + bool optimize) : conn_ (conn) { - init (text, std::strlen (text)); + init (text, std::strlen (text), sk, process, optimize); } statement (connection_type& conn, const char* text, - std::size_t text_size) + std::size_t text_size, + statement_kind sk, + const binding* process, + bool optimize) : conn_ (conn) { - init (text, text_size); + init (text, text_size, sk, process, optimize); } protected: @@ -128,7 +152,11 @@ namespace odb private: void - init (const char* text, std::size_t text_size); + init (const char* text, + std::size_t text_size, + statement_kind, + const binding* process, + bool optimize); // Doubly-linked list of active statements. // @@ -188,20 +216,28 @@ namespace odb public: 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); 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); // Common select interface expected by the generated code. @@ -283,10 +319,12 @@ namespace odb public: 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); // Return true if successful and false if the row is a duplicate. @@ -311,10 +349,12 @@ namespace odb public: 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); unsigned long long diff --git a/odb/sqlite/traits-calls.hxx b/odb/sqlite/traits-calls.hxx new file mode 100644 index 0000000..1e5bd8d --- /dev/null +++ b/odb/sqlite/traits-calls.hxx @@ -0,0 +1,215 @@ +// file : odb/sqlite/traits-calls.hxx +// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_SQLITE_TRAITS_CALLS_HXX +#define ODB_SQLITE_TRAITS_CALLS_HXX + +#include + +#include // std::size_t + +#include +#include +#include + +#include +#include + +namespace odb +{ + namespace sqlite + { + // + // 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 sqlite::bind bind_type; + + object_traits_calls (const schema_version_migration*) {} + + const schema_version_migration* + version () const {return 0;} + + static bool + grow (image_type& i, bool* t) + { + return traits::grow (i, t); + } + + 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 sqlite::bind bind_type; + + object_traits_calls (const schema_version_migration* svm): svm_ (*svm) {} + + const schema_version_migration* + version () const {return &svm_;} + + bool + grow (image_type& i, bool* t) const + { + return traits::grow (i, t, 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 sqlite::bind bind_type; + + view_traits_calls (const schema_version_migration*) {} + + static bool + grow (image_type& i, bool* t) + { + return traits::grow (i, t); + } + + 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 sqlite::bind bind_type; + + view_traits_calls (const schema_version_migration* svm): svm_ (*svm) {} + + bool + grow (image_type& i, bool* t) const + { + return traits::grow (i, t, 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_SQLITE_TRAITS_CALLS_HXX diff --git a/odb/sqlite/view-result.hxx b/odb/sqlite/view-result.hxx index fd189e6..0e05adb 100644 --- a/odb/sqlite/view-result.hxx +++ b/odb/sqlite/view-result.hxx @@ -9,6 +9,7 @@ #include // std::size_t +#include #include #include @@ -16,6 +17,7 @@ #include #include // query_base, query_params #include +#include namespace odb { @@ -40,7 +42,8 @@ namespace odb view_result_impl (const query_base&, const details::shared_ptr&, - statements_type&); + statements_type&, + const schema_version_migration*); virtual void load (view_type&); @@ -66,6 +69,7 @@ namespace odb details::shared_ptr params_; details::shared_ptr statement_; statements_type& statements_; + view_traits_calls tc_; }; } } diff --git a/odb/sqlite/view-result.txx b/odb/sqlite/view-result.txx index d22a440..d13b58e 100644 --- a/odb/sqlite/view-result.txx +++ b/odb/sqlite/view-result.txx @@ -37,11 +37,13 @@ namespace odb view_result_impl:: view_result_impl (const query_base& q, const details::shared_ptr& s, - statements_type& sts) + statements_type& sts, + const schema_version_migration* svm) : base_type (sts.connection ()), params_ (q.parameters ()), statement_ (s), - statements_ (sts) + statements_ (sts), + tc_ (svm) { } @@ -57,7 +59,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++; } @@ -66,13 +68,13 @@ namespace odb if (r == select_statement::truncated) { - 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_->reload (); @@ -80,7 +82,7 @@ namespace odb } view_traits::callback (this->db_, view, callback_event::pre_load); - view_traits::init (view, im, &this->db_); + tc_.init (view, im, &this->db_); view_traits::callback (this->db_, view, callback_event::post_load); } -- cgit v1.1