From ba8430c002627705d559b5dd9d78ae7611476520 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 26 Nov 2010 13:24:00 +0200 Subject: Add support for recursive object loading If an object of a type needs to be loaded recursively, then it is addded to the delayed loading list which is processed once the statements are unlocked. --- odb/mysql/object-statements.hxx | 128 +++++++++++++++++++++++++++++++++++++++- odb/mysql/object-statements.ixx | 54 +++++++++++++++++ odb/mysql/object-statements.txx | 49 ++++++++++++++- odb/mysql/result.hxx | 4 +- odb/mysql/result.txx | 16 ++++- 5 files changed, 241 insertions(+), 10 deletions(-) create mode 100644 odb/mysql/object-statements.ixx diff --git a/odb/mysql/object-statements.hxx b/odb/mysql/object-statements.hxx index 4c81a76..ae49e08 100644 --- a/odb/mysql/object-statements.hxx +++ b/odb/mysql/object-statements.hxx @@ -8,8 +8,12 @@ #include +#include +#include + #include #include +#include #include #include @@ -37,27 +41,56 @@ namespace odb return conn_; } + // Locking. + // + void + lock () + { + assert (!locked_); + locked_ = true; + } + + void + unlock () + { + assert (locked_); + locked_ = false; + } + + bool + locked () const + { + return locked_; + } + + public: virtual ~object_statements_base (); protected: object_statements_base (connection_type& conn) - : conn_ (conn) + : conn_ (conn), locked_ (false) { } protected: connection_type& conn_; + bool locked_; }; template class object_statements: public object_statements_base { public: - typedef odb::object_traits object_traits; + typedef T object_type; + typedef odb::object_traits object_traits; + typedef typename object_traits::id_type id_type; + typedef typename object_traits::pointer_type pointer_type; typedef typename object_traits::image_type image_type; typedef typename object_traits::id_image_type id_image_type; + typedef pointer_cache_traits object_cache_traits; + typedef typename object_traits::container_statement_cache_type container_statement_cache_type; @@ -67,8 +100,71 @@ namespace odb typedef mysql::update_statement update_statement_type; typedef mysql::delete_statement erase_statement_type; + // Automatic lock. + // + struct auto_lock + { + // Lock the statements unless they are already locked in which + // case subsequent calls to locked() will return false. + // + auto_lock (object_statements&); + + // Unlock the statemens if we are holding the lock and clear + // the delayed loads. This should only happen in case an + // exception is thrown. In normal circumstances, the user + // should call unlock() explicitly. + // + ~auto_lock (); + + // Return true if this auto_lock instance holds the lock. + // + bool + locked () const; + + // Unlock the statemens. + // + void + unlock (); + + private: + auto_lock (const auto_lock&); + auto_lock& operator= (const auto_lock&); + + private: + object_statements& s_; + bool locked_; + }; + + // + // object_statements (connection_type&); + // Delayed loading. + // + void + delay_load (const id_type& id, + object_type& obj, + const typename object_cache_traits::position_type& p) + { + delayed_.push_back (delayed_load (id, obj, p)); + } + + void + load_delayed () + { + assert (locked ()); + + if (!delayed_.empty ()) + load_delayed_ (); + } + + void + clear_delayed () + { + if (!delayed_.empty ()) + clear_delayed_ (); + } + // Object image. // image_type& @@ -210,6 +306,13 @@ namespace odb object_statements& operator= (const object_statements&); private: + void + load_delayed_ (); + + void + clear_delayed_ (); + + private: container_statement_cache_type container_statement_cache_; image_type image_; @@ -238,10 +341,31 @@ namespace odb details::shared_ptr find_; details::shared_ptr update_; details::shared_ptr erase_; + + // Delayed loading. + // + struct delayed_load + { + typedef typename object_cache_traits::position_type position_type; + + delayed_load () {} + delayed_load (const id_type& i, object_type& o, const position_type& p) + : id (i), obj (&o), pos (p) + { + } + + id_type id; + object_type* obj; + position_type pos; + }; + + typedef std::vector delayed_loads; + delayed_loads delayed_; }; } } +#include #include #include diff --git a/odb/mysql/object-statements.ixx b/odb/mysql/object-statements.ixx new file mode 100644 index 0000000..891499b --- /dev/null +++ b/odb/mysql/object-statements.ixx @@ -0,0 +1,54 @@ +// file : odb/mysql/object-statements.ixx +// author : Boris Kolpackov +// copyright : Copyright (c) 2005-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +namespace odb +{ + namespace mysql + { + // + // auto_lock + // + template + inline object_statements::auto_lock:: + auto_lock (object_statements& s) + : s_ (s) + { + if (!s_.locked ()) + { + s_.lock (); + locked_ = true; + } + else + locked_ = false; + } + + template + inline object_statements::auto_lock:: + ~auto_lock () + { + if (locked_) + { + s_.unlock (); + s_.clear_delayed (); + } + } + + template + inline bool object_statements::auto_lock:: + locked () const + { + return locked_; + } + + template + inline void object_statements::auto_lock:: + unlock () + { + assert (locked_); + s_.unlock (); + locked_ = false; + } + } +} diff --git a/odb/mysql/object-statements.txx b/odb/mysql/object-statements.txx index 32d67f1..750fec2 100644 --- a/odb/mysql/object-statements.txx +++ b/odb/mysql/object-statements.txx @@ -6,13 +6,15 @@ #include // std::size_t #include // std::memset +#include +#include + +#include + namespace odb { namespace mysql { - // object_statements - // - template object_statements:: object_statements (connection_type& conn) @@ -36,5 +38,46 @@ namespace odb for (std::size_t i (0); i < object_traits::out_column_count; ++i) out_image_bind_[i].error = out_image_error_ + i; } + + template + void object_statements:: + load_delayed_ () + { + // We should be careful here: the delayed vector can change + // from under us as a result of a recursive load. + // + database& db (connection ().database ()); + + while (!delayed_.empty ()) + { + delayed_load l (delayed_.back ()); + typename object_cache_traits::insert_guard g (l.pos); + delayed_.pop_back (); + + if (!object_traits::find_ (*this, l.id)) + throw object_not_persistent (); + + object_traits::init (*l.obj, image (), db); + g.release (); + } + } + + template + void object_statements:: + clear_delayed_ () + { + // Remove the objects from the session cache. + // + if (session::has_current ()) + { + for (typename delayed_loads::iterator i (delayed_.begin ()), + e (delayed_.end ()); i != e; ++i) + { + object_cache_traits::erase (i->pos); + } + } + + delayed_.clear (); + } } } diff --git a/odb/mysql/result.hxx b/odb/mysql/result.hxx index 336d46c..ec232a5 100644 --- a/odb/mysql/result.hxx +++ b/odb/mysql/result.hxx @@ -39,10 +39,10 @@ namespace odb object_statements& statements); virtual void - current (object_type&); + load (object_type&); virtual id_type - current_id (); + load_id (); virtual void next (); diff --git a/odb/mysql/result.txx b/odb/mysql/result.txx index bb1fe01..4b18abc 100644 --- a/odb/mysql/result.txx +++ b/odb/mysql/result.txx @@ -3,6 +3,8 @@ // copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC // license : GNU GPL v2; see accompanying LICENSE file +#include + namespace odb { namespace mysql @@ -26,14 +28,22 @@ namespace odb template void result_impl:: - current (object_type& x) + load (object_type& obj) { - object_traits::init (x, statements_.image (), this->database ()); + // This is a top-level call so the statements cannot be locked. + // + assert (!statements_.locked ()); + typename object_statements::auto_lock l (statements_); + + object_traits::init (obj, statements_.image (), this->database ()); + + statements_.load_delayed (); + l.unlock (); } template typename result_impl::id_type result_impl:: - current_id () + load_id () { return object_traits::id (statements_.image ()); } -- cgit v1.1