diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2010-11-26 13:24:00 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2010-11-26 13:24:00 +0200 |
commit | ba8430c002627705d559b5dd9d78ae7611476520 (patch) | |
tree | d629a51b81ed279a40172869a9130ce3ddbbe09c | |
parent | fba7f7853b4a9485fa2ee29189e4445252ccac97 (diff) |
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.
-rw-r--r-- | odb/mysql/object-statements.hxx | 128 | ||||
-rw-r--r-- | odb/mysql/object-statements.ixx | 54 | ||||
-rw-r--r-- | odb/mysql/object-statements.txx | 49 | ||||
-rw-r--r-- | odb/mysql/result.hxx | 4 | ||||
-rw-r--r-- | odb/mysql/result.txx | 16 |
5 files changed, 241 insertions, 10 deletions
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 <odb/pre.hxx> +#include <vector> +#include <cassert> + #include <odb/forward.hxx> #include <odb/traits.hxx> +#include <odb/cache-traits.hxx> #include <odb/mysql/mysql.hxx> #include <odb/mysql/version.hxx> @@ -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 <typename T> class object_statements: public object_statements_base { public: - typedef odb::object_traits<T> object_traits; + typedef T object_type; + typedef odb::object_traits<object_type> 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<pointer_type> 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_statement_type> find_; details::shared_ptr<update_statement_type> update_; details::shared_ptr<erase_statement_type> 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_load> delayed_loads; + delayed_loads delayed_; }; } } +#include <odb/mysql/object-statements.ixx> #include <odb/mysql/object-statements.txx> #include <odb/post.hxx> 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 <boris@codesynthesis.com> +// copyright : Copyright (c) 2005-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +namespace odb +{ + namespace mysql + { + // + // auto_lock + // + template <typename T> + inline object_statements<T>::auto_lock:: + auto_lock (object_statements& s) + : s_ (s) + { + if (!s_.locked ()) + { + s_.lock (); + locked_ = true; + } + else + locked_ = false; + } + + template <typename T> + inline object_statements<T>::auto_lock:: + ~auto_lock () + { + if (locked_) + { + s_.unlock (); + s_.clear_delayed (); + } + } + + template <typename T> + inline bool object_statements<T>::auto_lock:: + locked () const + { + return locked_; + } + + template <typename T> + inline void object_statements<T>::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 <cstddef> // std::size_t #include <cstring> // std::memset +#include <odb/session.hxx> +#include <odb/exceptions.hxx> + +#include <odb/mysql/connection.hxx> + namespace odb { namespace mysql { - // object_statements - // - template <typename T> object_statements<T>:: 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 <typename T> + void object_statements<T>:: + 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 <typename T> + void object_statements<T>:: + 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<object_type>& 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 <odb/cache-traits.hxx> + namespace odb { namespace mysql @@ -26,14 +28,22 @@ namespace odb template <typename T> void result_impl<T>:: - 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<object_type>::auto_lock l (statements_); + + object_traits::init (obj, statements_.image (), this->database ()); + + statements_.load_delayed (); + l.unlock (); } template <typename T> typename result_impl<T>::id_type result_impl<T>:: - current_id () + load_id () { return object_traits::id (statements_.image ()); } |