From a3372ca4e3eb1ba4e87dfa9ccb0c78c379935441 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 10 Sep 2013 14:10:45 +0200 Subject: Schema versioning support --- odb/oracle/container-statements.hxx | 25 ++- odb/oracle/container-statements.txx | 5 +- odb/oracle/database.cxx | 7 +- odb/oracle/forward.hxx | 4 +- odb/oracle/no-id-object-result.hxx | 6 +- odb/oracle/no-id-object-result.txx | 12 +- odb/oracle/no-id-object-statements.hxx | 1 + odb/oracle/polymorphic-object-result.hxx | 6 +- odb/oracle/polymorphic-object-result.txx | 25 +-- odb/oracle/polymorphic-object-statements.hxx | 23 ++- odb/oracle/polymorphic-object-statements.txx | 16 +- odb/oracle/query.cxx | 16 +- odb/oracle/section-statements.hxx | 3 + odb/oracle/simple-object-result.hxx | 6 +- odb/oracle/simple-object-result.txx | 12 +- odb/oracle/simple-object-statements.hxx | 21 ++- odb/oracle/simple-object-statements.txx | 19 ++- odb/oracle/statement-cache.hxx | 5 +- odb/oracle/statement-cache.txx | 15 ++ odb/oracle/statement.cxx | 223 +++++++++++++++++++++------ odb/oracle/statement.hxx | 55 ++++++- odb/oracle/traits-calls.hxx | 191 +++++++++++++++++++++++ odb/oracle/view-result.hxx | 6 +- odb/oracle/view-result.txx | 12 +- 24 files changed, 599 insertions(+), 115 deletions(-) create mode 100644 odb/oracle/traits-calls.hxx diff --git a/odb/oracle/container-statements.hxx b/odb/oracle/container-statements.hxx index 4da7af4..ef1733d 100644 --- a/odb/oracle/container-statements.hxx +++ b/odb/oracle/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,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_; } @@ -122,6 +135,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_, 4096)); // Hardcode a 4kB LOB prefetch size. @@ -161,6 +176,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_; @@ -260,7 +278,10 @@ namespace odb if (update_ == 0) update_.reset ( new (details::shared) update_statement_type ( - this->conn_, update_text_, update_image_binding_)); + this->conn_, + update_text_, + this->versioned_, // Process if versioned. + update_image_binding_)); return *update_; } diff --git a/odb/oracle/container-statements.txx b/odb/oracle/container-statements.txx index 814414a..f872c05 100644 --- a/odb/oracle/container-statements.txx +++ b/odb/oracle/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/oracle/database.cxx b/odb/oracle/database.cxx index 60dc4f4..a16ac82 100644 --- a/odb/oracle/database.cxx +++ b/odb/oracle/database.cxx @@ -309,7 +309,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/oracle/forward.hxx b/odb/oracle/forward.hxx index 6163fdf..1f7fafb 100644 --- a/odb/oracle/forward.hxx +++ b/odb/oracle/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/oracle/no-id-object-result.hxx b/odb/oracle/no-id-object-result.hxx index 150db3d..f3a695b 100644 --- a/odb/oracle/no-id-object-result.hxx +++ b/odb/oracle/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 use_copy_; typename object_traits::image_type* image_copy_; }; diff --git a/odb/oracle/no-id-object-result.txx b/odb/oracle/no-id-object-result.txx index c70661c..c58e0af 100644 --- a/odb/oracle/no-id-object-result.txx +++ b/odb/oracle/no-id-object-result.txx @@ -46,10 +46,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) { @@ -61,9 +63,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 // LOB data also comes from the copy. @@ -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++; } diff --git a/odb/oracle/no-id-object-statements.hxx b/odb/oracle/no-id-object-statements.hxx index 0510283..0be8dac 100644 --- a/odb/oracle/no-id-object-statements.hxx +++ b/odb/oracle/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/oracle/polymorphic-object-result.hxx b/odb/oracle/polymorphic-object-result.hxx index f23a199..6879645 100644 --- a/odb/oracle/polymorphic-object-result.hxx +++ b/odb/oracle/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 use_copy_; image_type* image_copy_; }; diff --git a/odb/oracle/polymorphic-object-result.txx b/odb/oracle/polymorphic-object-result.txx index a67dce4..19b4fa3 100644 --- a/odb/oracle/polymorphic-object-result.txx +++ b/odb/oracle/polymorphic-object-result.txx @@ -52,10 +52,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) { @@ -133,7 +135,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 // LOB data also comes from the copy. @@ -156,7 +158,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. @@ -167,7 +169,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; @@ -210,14 +212,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 ()); } @@ -242,14 +246,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++; } @@ -278,7 +284,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/oracle/polymorphic-object-statements.hxx b/odb/oracle/polymorphic-object-statements.hxx index 3a3cceb..81ea3c3 100644 --- a/odb/oracle/polymorphic-object-statements.hxx +++ b/odb/oracle/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_, 0)); // No LOB prefetch (discriminator cannot be LOB). @@ -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. @@ -287,6 +304,7 @@ namespace odb new (details::shared) insert_statement_type ( conn_, object_traits::persist_statement, + object_traits::versioned, // Process if versioned. insert_image_binding_, false)); @@ -304,6 +322,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], 4096)); // Hardcode a 4kB LOB prefetch size. @@ -319,6 +339,7 @@ namespace odb new (details::shared) update_statement_type ( conn_, object_traits::update_statement, + object_traits::versioned, // Process if versioned. update_image_binding_)); return *update_; diff --git a/odb/oracle/polymorphic-object-statements.txx b/odb/oracle/polymorphic-object-statements.txx index 83e88de..54ea3a8 100644 --- a/odb/oracle/polymorphic-object-statements.txx +++ b/odb/oracle/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,15 +117,17 @@ 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); + tc.init (obj, sts.image (), &db); sts.find_[0]->stream_result (); - 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/oracle/query.cxx b/odb/oracle/query.cxx index 9fbdc63..439d2c7 100644 --- a/odb/oracle/query.cxx +++ b/odb/oracle/query.cxx @@ -99,8 +99,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; @@ -184,7 +184,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; } @@ -244,7 +244,7 @@ namespace odb { case clause_part::kind_column: { - if (last != ' ' && last != '(') + if (last != ' ' && last != '\n' && last != '(') r += ' '; r += i->part; @@ -252,7 +252,7 @@ namespace odb } case clause_part::kind_param: { - if (last != ' ' && last != '(') + if (last != ' ' && last != '\n' && last != '(') r += ' '; ostringstream os; @@ -283,8 +283,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; @@ -292,7 +292,7 @@ namespace odb } case clause_part::kind_bool: { - if (last != ' ' && last != '(') + if (last != ' ' && last != '\n' && last != '(') r += ' '; // Oracle does not have TRUE and FALSE boolean literals (these diff --git a/odb/oracle/section-statements.hxx b/odb/oracle/section-statements.hxx index 8579be5..47e3bb7 100644 --- a/odb/oracle/section-statements.hxx +++ b/odb/oracle/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_, 4096)); // Hardcode a 4kB LOB prefetch size. @@ -112,6 +114,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/oracle/simple-object-result.hxx b/odb/oracle/simple-object-result.hxx index 19837e7..6ee56ba 100644 --- a/odb/oracle/simple-object-result.hxx +++ b/odb/oracle/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 use_copy_; typename object_traits::image_type* image_copy_; }; diff --git a/odb/oracle/simple-object-result.txx b/odb/oracle/simple-object-result.txx index 53d752d..114c919 100644 --- a/odb/oracle/simple-object-result.txx +++ b/odb/oracle/simple-object-result.txx @@ -48,10 +48,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) { @@ -71,7 +73,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 // LOB data also comes from the copy. @@ -94,8 +96,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); } @@ -129,7 +131,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/oracle/simple-object-statements.hxx b/odb/oracle/simple-object-statements.hxx index 17e6085..ff76e91 100644 --- a/odb/oracle/simple-object-statements.hxx +++ b/odb/oracle/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)); @@ -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_, 4096)); // Hardcode a 4kB LOB prefetch size. @@ -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_)); return *update_; @@ -452,14 +458,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/oracle/simple-object-statements.txx b/odb/oracle/simple-object-statements.txx index 6723b1c..61bbc5f 100644 --- a/odb/oracle/simple-object-statements.txx +++ b/odb/oracle/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 (); object_traits::callback (db, *l.obj, callback_event::pre_load); @@ -92,12 +96,15 @@ 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 (); - 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 @@ -113,7 +120,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/oracle/statement-cache.hxx b/odb/oracle/statement-cache.hxx index 2795fa4..10f08ea 100644 --- a/odb/oracle/statement-cache.hxx +++ b/odb/oracle/statement-cache.hxx @@ -29,7 +29,9 @@ namespace odb class LIBODB_ORACLE_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/oracle/statement-cache.txx b/odb/oracle/statement-cache.txx index a593674..ba747e2 100644 --- a/odb/oracle/statement-cache.txx +++ b/odb/oracle/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 oracle @@ -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/oracle/statement.cxx b/odb/oracle/statement.cxx index 13b9553..5ad867a 100644 --- a/odb/oracle/statement.cxx +++ b/odb/oracle/statement.cxx @@ -137,6 +137,9 @@ namespace odb statement:: ~statement () { + if (empty ()) + return; + { odb::tracer* t; if ((t = conn_.transaction_tracer ()) || @@ -192,22 +195,73 @@ namespace odb } statement:: - statement (connection_type& conn, const string& text) + statement (connection_type& conn, + const string& text, + statement_kind sk, + const binding* process, + bool optimize) : conn_ (conn), udata_ (0), usize_ (0) { - init (text.c_str (), text.size ()); + init (text.c_str (), text.size (), sk, process, optimize); } statement:: - statement (connection_type& conn, const char* text) + statement (connection_type& conn, + const char* text, + statement_kind sk, + const binding* process, + bool optimize) : conn_ (conn), udata_ (0), usize_ (0) { - init (text, strlen (text)); + init (text, strlen (text), sk, process, optimize); } void statement:: - init (const char* text, size_t text_size) + init (const char* text, + 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, + false); // No AS in JOINs. + 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; + OCIError* err (conn_.error_handle ()); OCIStmt* handle (0); @@ -254,7 +308,7 @@ namespace odb return reinterpret_cast (s); } - void statement:: + ub4 statement:: bind_param (bind* b, size_t n) { // Figure out how many unbind elements we will need and allocate them. @@ -264,6 +318,9 @@ namespace odb for (size_t i (0); i < n; ++i) { + if (b[i].buffer == 0) // Skip NULL entries. + continue; + switch (b[i].type) { case bind::timestamp: @@ -301,13 +358,14 @@ namespace odb OCIError* err (conn_.error_handle ()); OCIEnv* env (conn_.database ().environment ()); - // The parameter position in OCIBindByPos is specified as a 1-based - // index. - // - n++; - - for (ub4 i (1); i < n; ++i, ++b) + ub4 i (0); + for (bind* end (b + n); b != end; ++b) { + if (b->buffer == 0) // Skip NULL entries. + continue; + + i++; // Column index is 1-based. + void* value; sb4 capacity; ub2* size (0); @@ -617,9 +675,11 @@ namespace odb translate_error (err, r); } } + + return i; } - void statement:: + ub4 statement:: bind_result (bind* b, size_t c, size_t p) { ODB_POTENTIALLY_UNUSED (p); @@ -628,8 +688,14 @@ namespace odb OCIError* err (conn_.error_handle ()); OCIEnv* env (conn_.database ().environment ()); - for (size_t i (1); i <= c; ++i, ++b) + ub4 i (0); + for (bind* end (b + c); b != end; ++b) { + if (b->buffer == 0) // Skip NULL entries. + continue; + + i++; // Column index is 1-based. + void* value; sb4 capacity; ub2* size (0); @@ -816,6 +882,8 @@ namespace odb translate_error (err, r); } } + + return i; } void statement:: @@ -826,8 +894,14 @@ namespace odb sword r; OCIEnv* env (conn_.database ().environment ()); - for (size_t i (1); i <= c; ++i, ++b) + ub4 i (0); + for (bind* end (b + c); b != end; ++b) { + if (b->buffer == 0) // Skip NULL entries. + continue; + + i++; // Column index is 1-based. + void* value; switch (b->type) @@ -971,8 +1045,11 @@ namespace odb { OCIError* err (conn_.error_handle ()); - for (size_t i (0); i < c; ++i, ++b) + for (bind* end (b + c); b != end; ++b) { + if (b->buffer == 0) // Skip NULL entries. + continue; + // Only stream if the bind specifies a LOB type. // if (b->type == bind::blob || @@ -1091,14 +1168,20 @@ namespace odb generic_statement:: generic_statement (connection_type& conn, const string& text) - : statement (conn, text), bound_ (false) + : statement (conn, + text, statement_generic, + 0, false), + bound_ (false) { init (); } generic_statement:: generic_statement (connection_type& conn, const char* text) - : statement (conn, text), bound_ (false) + : statement (conn, + text, statement_generic, + 0, false), + bound_ (false) { init (); } @@ -1270,65 +1353,93 @@ namespace odb select_statement:: select_statement (connection_type& conn, const string& text, + bool process, + bool optimize, binding& param, binding& result, size_t lob_prefetch_size) - : statement (conn, text), + : statement (conn, + text, statement_select, + (process ? &result : 0), optimize), result_ (result), - result_version_ (0), lob_prefetch_size_ (lob_prefetch_size), done_ (true) { - bind_param (param.bind, param.count); - bind_result (result.bind, result.count, lob_prefetch_size); - result_version_ = result_.version; + if (!empty ()) + { + bind_param (param.bind, param.count); + result_count_ = bind_result ( + result.bind, result.count, lob_prefetch_size); + result_version_ = result_.version; + } } select_statement:: select_statement (connection_type& conn, const char* text, + bool process, + bool optimize, binding& param, binding& result, size_t lob_prefetch_size) - : statement (conn, text), + : statement (conn, + text, statement_select, + (process ? &result : 0), optimize), result_ (result), - result_version_ (0), lob_prefetch_size_ (lob_prefetch_size), done_ (true) { - bind_param (param.bind, param.count); - bind_result (result.bind, result.count, lob_prefetch_size); - result_version_ = result_.version; + if (!empty ()) + { + bind_param (param.bind, param.count); + result_count_ = bind_result ( + result.bind, result.count, lob_prefetch_size); + result_version_ = result_.version; + } } select_statement:: select_statement (connection_type& conn, const string& text, + bool process, + bool optimize, binding& result, size_t lob_prefetch_size) - : statement (conn, text), + : statement (conn, + text, statement_select, + (process ? &result : 0), optimize), result_ (result), - result_version_ (0), lob_prefetch_size_ (lob_prefetch_size), done_ (true) { - bind_result (result.bind, result.count, lob_prefetch_size); - result_version_ = result_.version; + if (!empty ()) + { + result_count_ = bind_result ( + result.bind, result.count, lob_prefetch_size); + result_version_ = result_.version; + } } select_statement:: select_statement (connection_type& conn, const char* text, + bool process, + bool optimize, binding& result, size_t lob_prefetch_size) - : statement (conn, text), + : statement (conn, + text, statement_select, + (process ? &result : 0), optimize), result_ (result), - result_version_ (0), lob_prefetch_size_ (lob_prefetch_size), done_ (true) { - bind_result (result.bind, result.count, lob_prefetch_size); - result_version_ = result_.version; + if (!empty ()) + { + result_count_ = bind_result ( + result.bind, result.count, lob_prefetch_size); + result_version_ = result_.version; + } } void select_statement:: @@ -1374,7 +1485,7 @@ namespace odb // of this assertion is a native view with a number of data members // not matching the number of columns in the SELECT-list. // - assert (n == result_.count); + assert (n == result_count_); #endif } @@ -1495,9 +1606,12 @@ namespace odb insert_statement:: insert_statement (connection_type& conn, const string& text, + bool process, binding& param, bool returning) - : statement (conn, text) + : statement (conn, + text, statement_insert, + (process ? ¶m : 0), false) { init (param, returning); } @@ -1505,9 +1619,12 @@ namespace odb insert_statement:: insert_statement (connection_type& conn, const char* text, + bool process, binding& param, bool returning) - : statement (conn, text) + : statement (conn, + text, statement_insert, + (process ? ¶m : 0), false) { init (param, returning); } @@ -1515,7 +1632,7 @@ namespace odb void insert_statement:: init (binding& param, bool returning) { - bind_param (param.bind, param.count); + ub4 param_count (bind_param (param.bind, param.count)); if (returning) { @@ -1525,7 +1642,7 @@ namespace odb sword r (OCIBindByPos (stmt_, &h, err, - param.count + 1, + param_count + 1, 0, #if (OCI_MAJOR_VERSION == 11 && OCI_MINOR_VERSION >=2) \ || OCI_MAJOR_VERSION > 11 @@ -1636,19 +1753,27 @@ namespace odb update_statement:: update_statement (connection_type& conn, const string& text, + bool process, binding& param) - : statement (conn, text) + : statement (conn, + text, statement_update, + (process ? ¶m : 0), false) { - bind_param (param.bind, param.count); + if (!empty ()) + bind_param (param.bind, param.count); } update_statement:: update_statement (connection_type& conn, const char* text, + bool process, binding& param) - : statement (conn, text) + : statement (conn, + text, statement_update, + (process ? ¶m : 0), false) { - bind_param (param.bind, param.count); + if (!empty ()) + bind_param (param.bind, param.count); } unsigned long long update_statement:: @@ -1707,7 +1832,9 @@ namespace odb delete_statement (connection_type& conn, const string& text, binding& param) - : statement (conn, text) + : statement (conn, + text, statement_delete, + 0, false) { bind_param (param.bind, param.count); } @@ -1716,7 +1843,9 @@ namespace odb delete_statement (connection_type& conn, const char* text, binding& param) - : statement (conn, text) + : statement (conn, + text, statement_delete, + 0, false) { bind_param (param.bind, param.count); } diff --git a/odb/oracle/statement.hxx b/odb/oracle/statement.hxx index d246992..cf336c4 100644 --- a/odb/oracle/statement.hxx +++ b/odb/oracle/statement.hxx @@ -48,13 +48,39 @@ 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); + // 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); 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); protected: struct unbind @@ -66,16 +92,18 @@ namespace odb // Bind parameters for this statement. This function must only // be called once. Multiple calls to it will result in memory - // leaks due to lost OCIBind resources. + // leaks due to lost OCIBind resources. Return the actual number + // of columns bound. // - void + ub4 bind_param (bind*, std::size_t count); // Bind results for this statement. This function must only be // called once. Multiple calls to it will result in memory leaks - // due to lost OCIDefine resources. + // due to lost OCIDefine resources. Return the actual number of + // columns bound. // - void + ub4 bind_result (bind*, std::size_t count, std::size_t lob_prefetch_size = 0); @@ -146,23 +174,31 @@ namespace odb select_statement (connection_type& conn, const std::string& text, + bool process_text, + bool optimize_text, binding& param, binding& result, std::size_t lob_prefetch_size = 0); select_statement (connection_type& conn, const char* text, + bool process_text, + bool optimize_text, binding& param, binding& result, std::size_t lob_prefetch_size = 0); select_statement (connection_type& conn, const std::string& text, + bool process_text, + bool optimize_text, binding& result, std::size_t lob_prefetch_size = 0); select_statement (connection_type& conn, const char* text, + bool process_text, + bool optimize_text, binding& result, std::size_t lob_prefetch_size = 0); @@ -197,6 +233,7 @@ namespace odb private: binding& result_; std::size_t result_version_; + ub4 result_count_; // Actual number of bound columns. const std::size_t lob_prefetch_size_; bool done_; }; @@ -222,11 +259,13 @@ namespace odb insert_statement (connection_type& conn, const std::string& text, + bool process_text, binding& param, bool returning); insert_statement (connection_type& conn, const char* text, + bool process_text, binding& param, bool returning); @@ -282,10 +321,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); unsigned long long diff --git a/odb/oracle/traits-calls.hxx b/odb/oracle/traits-calls.hxx new file mode 100644 index 0000000..e15e616 --- /dev/null +++ b/odb/oracle/traits-calls.hxx @@ -0,0 +1,191 @@ +// file : odb/oracle/traits-calls.hxx +// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_ORACLE_TRAITS_CALLS_HXX +#define ODB_ORACLE_TRAITS_CALLS_HXX + +#include + +#include // std::size_t + +#include +#include +#include + +#include +#include + +namespace odb +{ + namespace oracle + { + // + // 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 oracle::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 oracle::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 oracle::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 oracle::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_ORACLE_TRAITS_CALLS_HXX diff --git a/odb/oracle/view-result.hxx b/odb/oracle/view-result.hxx index 0adf0fe..424728b 100644 --- a/odb/oracle/view-result.hxx +++ b/odb/oracle/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 use_copy_; typename view_traits::image_type* image_copy_; }; diff --git a/odb/oracle/view-result.txx b/odb/oracle/view-result.txx index df2a833..402a571 100644 --- a/odb/oracle/view-result.txx +++ b/odb/oracle/view-result.txx @@ -46,10 +46,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) { @@ -61,9 +63,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 // LOB data also comes from the copy. @@ -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++; } -- cgit v1.1