diff options
-rw-r--r-- | odb/cache-traits.hxx | 152 | ||||
-rw-r--r-- | odb/database.hxx | 70 | ||||
-rw-r--r-- | odb/database.ixx | 177 | ||||
-rw-r--r-- | odb/database.txx | 115 | ||||
-rw-r--r-- | odb/exceptions.cxx | 18 | ||||
-rw-r--r-- | odb/exceptions.hxx | 24 | ||||
-rw-r--r-- | odb/makefile | 1 | ||||
-rw-r--r-- | odb/result.hxx | 43 | ||||
-rw-r--r-- | odb/result.txx | 72 | ||||
-rw-r--r-- | odb/session.cxx | 72 | ||||
-rw-r--r-- | odb/session.hxx | 159 | ||||
-rw-r--r-- | odb/session.ixx | 65 | ||||
-rw-r--r-- | odb/session.txx | 116 | ||||
-rw-r--r-- | odb/traits.hxx | 61 |
14 files changed, 1103 insertions, 42 deletions
diff --git a/odb/cache-traits.hxx b/odb/cache-traits.hxx new file mode 100644 index 0000000..5d32624 --- /dev/null +++ b/odb/cache-traits.hxx @@ -0,0 +1,152 @@ +// file : odb/cache-traits.hxx +// author : Boris Kolpackov <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_CACHE_TRAITS_HXX +#define ODB_CACHE_TRAITS_HXX + +#include <odb/pre.hxx> + +#include <odb/traits.hxx> +#include <odb/session.hxx> +#include <odb/pointer-traits.hxx> + +namespace odb +{ + // Caching traits for objects passed by pointer. + // + template <typename P, pointer_kind = pointer_traits<P>::kind> + struct pointer_cache_traits + { + typedef P pointer_type; + typedef typename pointer_traits<pointer_type>::element_type element_type; + typedef typename object_traits<element_type>::id_type id_type; + typedef session::object_position<element_type> position_type; + + struct insert_guard + { + insert_guard (const position_type& pos): pos_ (pos) {} + + ~insert_guard () + { + if (pos_.map_ != 0) + session::current ().erase<element_type> (pos_); + } + + void + release () {pos_.map_ = 0;} + + private: + position_type pos_; + }; + + static position_type + insert (database& db, const id_type& id, const pointer_type& p) + { + if (session::has_current ()) + return session::current ().insert<element_type> (db, id, p); + else + return position_type (); + } + + static pointer_type + find (database& db, const id_type& id) + { + if (session::has_current ()) + return session::current ().find<element_type> (db, id); + else + return pointer_type (); + } + + static void + erase (database& db, const id_type& id) + { + if (session::has_current ()) + session::current ().erase<element_type> (db, id); + } + }; + + // Unique pointers don't work with the object cache. + // + template <typename P> + struct pointer_cache_traits<P, pk_unique> + { + typedef P pointer_type; + typedef typename pointer_traits<pointer_type>::element_type element_type; + typedef typename object_traits<element_type>::id_type id_type; + struct position_type {}; + + struct insert_guard + { + insert_guard (const position_type&) {} + void release () {} + }; + + static position_type + insert (database&, const id_type&, const pointer_type&) + { + return position_type (); + } + + static pointer_type + find (database&, const id_type&) { return pointer_type (); } + + static void + erase (database&, const id_type&) {} + }; + + // Caching traits for objects passed by reference. Only if the object + // pointer kind is naked do we add the object to the session. + // + template <typename T, + pointer_kind = + pointer_traits<typename object_traits<T>::pointer_type>::kind> + struct reference_cache_traits + { + typedef T element_type; + typedef typename object_traits<element_type>::pointer_type pointer_type; + typedef typename object_traits<element_type>::id_type id_type; + + struct position_type {}; + + struct insert_guard + { + insert_guard (const position_type&) {} + void release () {} + }; + + static position_type + insert (database&, const id_type&, element_type&) + { + return position_type (); + } + }; + + template <typename T> + struct reference_cache_traits<T, pk_naked> + { + typedef T element_type; + typedef typename object_traits<element_type>::pointer_type pointer_type; + typedef typename object_traits<element_type>::id_type id_type; + + typedef + typename pointer_cache_traits<pointer_type>::position_type + position_type; + + typedef + typename pointer_cache_traits<pointer_type>::insert_guard + insert_guard; + + static position_type + insert (database& db, const id_type& id, element_type& obj) + { + pointer_type p (&obj); + return pointer_cache_traits<pointer_type>::insert (db, id, p); + } + }; +} + +#include <odb/post.hxx> + +#endif // ODB_CACHE_TRAITS_HXX diff --git a/odb/database.hxx b/odb/database.hxx index 7db04c8..e7ea91a 100644 --- a/odb/database.hxx +++ b/odb/database.hxx @@ -36,11 +36,23 @@ namespace odb // template <typename T> typename object_traits<T>::id_type - persist (const T& object); + persist (T& object); template <typename T> typename object_traits<T>::id_type - persist (T& object); + persist (T* obj_ptr); + + template <typename T, template <typename> class P> + typename object_traits<T>::id_type + persist (const P<T>& obj_ptr); + + template <typename T, template <typename> class P> + typename object_traits<T>::id_type + persist (P<T>& obj_ptr); + + template <typename T> + typename object_traits<T>::id_type + persist (const typename object_traits<T>::pointer_type& obj_ptr); // Throw object_not_persistent if not found. // @@ -66,18 +78,50 @@ namespace odb // template <typename T> void - update (const T& object); + update (T& object); + + template <typename T> + void + update (T* obj_ptr); + + template <typename T, template <typename> class P> + void + update (const P<T>& obj_ptr); + + template <typename T, template <typename> class P> + void + update (P<T>& obj_ptr); + + template <typename T> + void + update (const typename object_traits<T>::pointer_type& obj_ptr); // Make the object transient. Throw object_not_persistent if not // found. // template <typename T> void - erase (const T& object); + erase (const typename object_traits<T>::id_type& id); template <typename T> void - erase (const typename object_traits<T>::id_type& id); + erase (T& object); + + template <typename T> + void + erase (T* obj_ptr); + + template <typename T, template <typename> class P> + void + erase (const P<T>& obj_ptr); + + template <typename T, template <typename> class P> + void + erase (P<T>& obj_ptr); + + template <typename T> + void + erase (const typename object_traits<T>::pointer_type& obj_ptr); // Query API. // @@ -95,7 +139,8 @@ namespace odb template <typename T> result<T> - query (const odb::query<T>&, bool cache = true); + query (const odb::query<typename object_traits<T>::object_type>&, + bool cache = true); // Transaction API. // @@ -106,6 +151,19 @@ namespace odb protected: database (); + protected: + template <typename T> + typename object_traits<T>::id_type + persist_ (const typename object_traits<T>::pointer_type&); + + template <typename T> + void + update_ (const typename object_traits<T>::pointer_type&); + + template <typename T> + void + erase_ (const typename object_traits<T>::pointer_type&); + private: database (const database&); database& operator= (const database&); diff --git a/odb/database.ixx b/odb/database.ixx index 8085210..86ccd15 100644 --- a/odb/database.ixx +++ b/odb/database.ixx @@ -11,30 +11,197 @@ namespace odb } template <typename T> + inline typename object_traits<T>::id_type database:: + persist (T* p) + { + typedef typename object_traits<T>::pointer_type object_pointer; + + // The passed pointer should be the same or implicit-convertible + // to the object pointer. This way we make sure the object pointer + // does not assume ownership of the passed object. + // + const object_pointer& pobj (p); + + return persist_<T> (pobj); + } + + template <typename T, template <typename> class P> + inline typename object_traits<T>::id_type database:: + persist (const P<T>& p) + { + typedef typename object_traits<T>::pointer_type object_pointer; + + // The passed pointer should be the same or implicit-convertible + // to the object pointer. This way we make sure the object pointer + // does not assume ownership of the passed object. + // + const object_pointer& pobj (p); + + return persist_<T> (pobj); + } + + template <typename T, template <typename> class P> + inline typename object_traits<T>::id_type database:: + persist (P<T>& p) + { + const P<T>& cr (p); + return persist<T, P> (cr); + } + + template <typename T> + inline typename object_traits<T>::id_type database:: + persist (const typename object_traits<T>::pointer_type& pobj) + { + return persist_<T> (pobj); + } + + template <typename T> inline void database:: - erase (const T& obj) + update (T* p) { - erase<T> (object_traits<T>::id (obj)); + typedef typename object_traits<T>::pointer_type object_pointer; + + // The passed pointer should be the same or implicit-convertible + // to the object pointer. This way we make sure the object pointer + // does not assume ownership of the passed object. + // + const object_pointer& pobj (p); + + update_<T> (pobj); + } + + template <typename T, template <typename> class P> + inline void database:: + update (const P<T>& p) + { + typedef typename object_traits<T>::pointer_type object_pointer; + + // The passed pointer should be the same or implicit-convertible + // to the object pointer. This way we make sure the object pointer + // does not assume ownership of the passed object. + // + const object_pointer& pobj (p); + + update_<T> (pobj); + } + + template <typename T, template <typename> class P> + inline void database:: + update (P<T>& p) + { + const P<T>& cr (p); + update<T, P> (cr); + } + + template <typename T> + inline void database:: + update (const typename object_traits<T>::pointer_type& pobj) + { + update_<T> (pobj); + } + + template <typename T> + inline void database:: + erase (T& obj) + { + // T can be const T while object_type will always be T. + // + typedef typename odb::object_traits<T>::object_type object_type; + typedef odb::object_traits<object_type> object_traits; + + erase<T> (object_traits::id (obj)); + } + + template <typename T> + inline void database:: + erase (T* p) + { + typedef typename object_traits<T>::pointer_type object_pointer; + + // The passed pointer should be the same or implicit-convertible + // to the object pointer. This way we make sure the object pointer + // does not assume ownership of the passed object. + // + const object_pointer& pobj (p); + + erase_<T> (pobj); + } + + template <typename T, template <typename> class P> + inline void database:: + erase (const P<T>& p) + { + typedef typename object_traits<T>::pointer_type object_pointer; + + // The passed pointer should be the same or implicit-convertible + // to the object pointer. This way we make sure the object pointer + // does not assume ownership of the passed object. + // + const object_pointer& pobj (p); + + erase_<T> (pobj); + } + + template <typename T, template <typename> class P> + inline void database:: + erase (P<T>& p) + { + const P<T>& cr (p); + erase<T, P> (cr); + } + + template <typename T> + inline void database:: + erase (const typename object_traits<T>::pointer_type& pobj) + { + erase_<T> (pobj); + } + + template <typename T> + inline void database:: + erase_ (const typename object_traits<T>::pointer_type& pobj) + { + // T can be const T while object_type will always be T. + // + typedef typename odb::object_traits<T>::object_type object_type; + typedef odb::object_traits<object_type> object_traits; + + typedef typename odb::object_traits<T>::pointer_type pointer_type; + typedef odb::pointer_traits<pointer_type> pointer_traits; + + erase<T> (object_traits::id (pointer_traits::get_ref (pobj))); } template <typename T> inline result<T> database:: query (bool cache) { - return query (odb::query<T> (), cache); + // T can be const T while object_type will always be T. + // + typedef typename odb::object_traits<T>::object_type object_type; + + return query<T> (odb::query<object_type> (), cache); } template <typename T> inline result<T> database:: query (const char* q, bool cache) { - return query (odb::query<T> (q), cache); + // T can be const T while object_type will always be T. + // + typedef typename odb::object_traits<T>::object_type object_type; + + return query<T> (odb::query<object_type> (q), cache); } template <typename T> inline result<T> database:: query (const std::string& q, bool cache) { - return query (odb::query<T> (q), cache); + // T can be const T while object_type will always be T. + // + typedef typename odb::object_traits<T>::object_type object_type; + + return query<T> (odb::query<object_type> (q), cache); } } diff --git a/odb/database.txx b/odb/database.txx index 31e9153..d98028d 100644 --- a/odb/database.txx +++ b/odb/database.txx @@ -5,43 +5,62 @@ #include <odb/exceptions.hxx> #include <odb/transaction.hxx> +#include <odb/session.hxx> +#include <odb/cache-traits.hxx> +#include <odb/pointer-traits.hxx> namespace odb { template <typename T> typename object_traits<T>::id_type database:: - persist (const T& obj) + persist (T& obj) { - typedef object_traits<T> traits; + // T can be const T while object_type will always be T. + // + typedef typename odb::object_traits<T>::object_type object_type; + typedef odb::object_traits<object_type> object_traits; if (!transaction::has_current ()) throw not_in_transaction (); - traits::persist (*this, obj); - return traits::id (obj); + object_traits::persist (*this, obj); + const typename object_traits::id_type& id (object_traits::id (obj)); + reference_cache_traits<T>::insert (*this, id, obj); + return id; } template <typename T> typename object_traits<T>::id_type database:: - persist (T& obj) + persist_ (const typename object_traits<T>::pointer_type& pobj) { - typedef object_traits<T> traits; + // T can be const T while object_type will always be T. + // + typedef typename odb::object_traits<T>::object_type object_type; + typedef odb::object_traits<object_type> object_traits; + + typedef typename odb::object_traits<T>::pointer_type pointer_type; + typedef odb::pointer_traits<pointer_type> pointer_traits; if (!transaction::has_current ()) throw not_in_transaction (); - traits::persist (*this, obj); - return traits::id (obj); + T& obj (pointer_traits::get_ref (pobj)); + object_traits::persist (*this, obj); + const typename object_traits::id_type& id (object_traits::id (obj)); + pointer_cache_traits<pointer_type>::insert (*this, id, pobj); + return id; } template <typename T> typename object_traits<T>::pointer_type database:: load (const typename object_traits<T>::id_type& id) { - typedef object_traits<T> traits; - typename traits::pointer_type r (find<T> (id)); + typedef typename object_traits<T>::pointer_type pointer_type; + typedef odb::pointer_traits<pointer_type> pointer_traits; + + pointer_type r (find<T> (id)); - if (traits::pointer_traits::null_ptr (r)) + if (pointer_traits::null_ptr (r)) throw object_not_persistent (); return r; @@ -59,53 +78,113 @@ namespace odb typename object_traits<T>::pointer_type database:: find (const typename object_traits<T>::id_type& id) { + // T can be const T while object_type will always be T. + // + typedef typename odb::object_traits<T>::object_type object_type; + typedef odb::object_traits<object_type> object_traits; + + typedef typename odb::object_traits<T>::pointer_type pointer_type; + typedef odb::pointer_traits<pointer_type> pointer_traits; + + // First check the session. + // + { + pointer_type p ( + pointer_cache_traits<pointer_type>::find (*this, id)); + + if (!pointer_traits::null_ptr (p)) + return p; + } + if (!transaction::has_current ()) throw not_in_transaction (); // Compiler error pointing here? Perhaps the object doesn't have the // default constructor? // - return object_traits<T>::find (*this, id); + return pointer_type (object_traits::find (*this, id)); } template <typename T> bool database:: find (const typename object_traits<T>::id_type& id, T& obj) { + // T can be const T while object_type will always be T. + // + typedef typename odb::object_traits<T>::object_type object_type; + typedef odb::object_traits<object_type> object_traits; + + if (!transaction::has_current ()) + throw not_in_transaction (); + + return object_traits::find (*this, id, obj); + } + + template <typename T> + void database:: + update (T& obj) + { + // T can be const T while object_type will always be T. + // + typedef typename odb::object_traits<T>::object_type object_type; + typedef odb::object_traits<object_type> object_traits; + if (!transaction::has_current ()) throw not_in_transaction (); - return object_traits<T>::find (*this, id, obj); + object_traits::update (*this, obj); } template <typename T> void database:: - update (const T& obj) + update_ (const typename object_traits<T>::pointer_type& pobj) { + // T can be const T while object_type will always be T. + // + typedef typename odb::object_traits<T>::object_type object_type; + typedef odb::object_traits<object_type> object_traits; + + typedef typename odb::object_traits<T>::pointer_type pointer_type; + typedef odb::pointer_traits<pointer_type> pointer_traits; + if (!transaction::has_current ()) throw not_in_transaction (); - object_traits<T>::update (*this, obj); + object_traits::update (*this, pointer_traits::get_ref (pobj)); } template <typename T> void database:: erase (const typename object_traits<T>::id_type& id) { + // T can be const T while object_type will always be T. + // + typedef typename odb::object_traits<T>::object_type object_type; + typedef odb::object_traits<object_type> object_traits; + + typedef typename odb::object_traits<T>::pointer_type pointer_type; + if (!transaction::has_current ()) throw not_in_transaction (); - object_traits<T>::erase (*this, id); + object_traits::erase (*this, id); + pointer_cache_traits<pointer_type>::erase (*this, id); } template <typename T> result<T> database:: - query (const odb::query<T>& q, bool cache) + query (const odb::query<typename object_traits<T>::object_type>& q, + bool cache) { + // T can be const T while object_type will always be T. + // + typedef typename odb::object_traits<T>::object_type object_type; + typedef odb::object_traits<object_type> object_traits; + if (!transaction::has_current ()) throw not_in_transaction (); - result<T> r (object_traits<T>::query (*this, q)); + result<T> r (object_traits::template query<T> (*this, q)); if (cache) r.cache (); diff --git a/odb/exceptions.cxx b/odb/exceptions.cxx index c480125..a41f719 100644 --- a/odb/exceptions.cxx +++ b/odb/exceptions.cxx @@ -25,6 +25,24 @@ namespace odb return "transaction already committed or rolled back"; } + const char* already_in_session:: + what () const throw () + { + return "session already in effect in this thread"; + } + + const char* not_in_session:: + what () const throw () + { + return "session not in effect in this thread"; + } + + const char* const_object:: + what () const throw () + { + return "object cached in session is const"; + } + const char* deadlock:: what () const throw () { diff --git a/odb/exceptions.hxx b/odb/exceptions.hxx index 91fa5ab..26550cb 100644 --- a/odb/exceptions.hxx +++ b/odb/exceptions.hxx @@ -14,6 +14,8 @@ namespace odb { + // Transaction exceptions. + // struct LIBODB_EXPORT already_in_transaction: odb::exception { virtual const char* @@ -32,6 +34,28 @@ namespace odb what () const throw (); }; + // Session exceptions. + // + struct LIBODB_EXPORT already_in_session: odb::exception + { + virtual const char* + what () const throw (); + }; + + struct LIBODB_EXPORT not_in_session: odb::exception + { + virtual const char* + what () const throw (); + }; + + struct LIBODB_EXPORT const_object: odb::exception + { + virtual const char* + what () const throw (); + }; + + // Database operations exceptions. + // struct LIBODB_EXPORT deadlock: odb::exception { virtual const char* diff --git a/odb/makefile b/odb/makefile index 6e8790f..2569de5 100644 --- a/odb/makefile +++ b/odb/makefile @@ -8,6 +8,7 @@ include $(dir $(lastword $(MAKEFILE_LIST)))../build/bootstrap.make cxx := \ exceptions.cxx \ database.cxx \ +session.cxx \ transaction.cxx # Implementation details. diff --git a/odb/result.hxx b/odb/result.hxx index 42acb0e..e87374e 100644 --- a/odb/result.hxx +++ b/odb/result.hxx @@ -30,15 +30,30 @@ namespace odb public: virtual ~result_impl (); - result_impl () : end_ (false), current_ () {} protected: friend class result<T>; friend class result_iterator<T>; - typedef object_traits<T> traits; - typedef typename traits::pointer_type pointer_type; - typedef typename traits::pointer_traits pointer_traits; + typedef odb::database database_type; + + typedef typename odb::object_traits<T>::pointer_type pointer_type; + typedef odb::pointer_traits<pointer_type> pointer_traits; + + typedef typename odb::object_traits<T>::object_type object_type; + typedef typename odb::object_traits<T>::id_type id_type; + typedef odb::object_traits<object_type> object_traits; + + result_impl (database_type& db) + : end_ (false), db_ (db), current_ () + { + } + + database_type& + database () const + { + return db_; + } // To make this work with all kinds of pointers (naked, std::auto_ptr, // shared), we need to make sure we don't make any copies of the @@ -62,7 +77,10 @@ namespace odb protected: virtual void - current (T&) = 0; + current (object_type&) = 0; + + virtual id_type + current_id () = 0; virtual void next () = 0; @@ -84,6 +102,7 @@ namespace odb bool end_; private: + database_type& db_; pointer_type current_; typename pointer_traits::guard_type guard_; }; @@ -98,6 +117,11 @@ namespace odb typedef std::ptrdiff_t difference_type; typedef std::input_iterator_tag iterator_category; + // T might be const T, but object_type is always T. + // + typedef typename object_traits<T>::object_type object_type; + typedef typename object_traits<T>::id_type id_type; + public: explicit result_iterator (result_impl<T>* res = 0) @@ -150,10 +174,7 @@ namespace odb } void - load (T& x) - { - res_->current (x); - } + load (object_type&); public: bool @@ -163,7 +184,9 @@ namespace odb } private: - typedef typename object_traits<T>::pointer_traits pointer_traits; + typedef + odb::pointer_traits<typename object_traits<T>::pointer_type> + pointer_traits; result_impl<T>* res_; }; diff --git a/odb/result.txx b/odb/result.txx index 3068890..603ee54 100644 --- a/odb/result.txx +++ b/odb/result.txx @@ -3,6 +3,9 @@ // copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC // license : GNU GPL v2; see accompanying LICENSE file +#include <odb/session.hxx> +#include <odb/cache-traits.hxx> + namespace odb { template <typename T> @@ -15,12 +18,77 @@ namespace odb typename result_impl<T>::pointer_type& result_impl<T>:: current () { + typedef typename object_traits::pointer_type unrestricted_pointer_type; + typedef typename object_traits::pointer_traits unrestricted_pointer_traits; + if (pointer_traits::null_ptr (current_) && !end_) { - current (traits::create ()); - current (pointer_traits::get_ref (current_)); + if (!session::has_current ()) + { + // Non-const pointer. + // + unrestricted_pointer_type p (object_traits::create ()); + object_type& r (unrestricted_pointer_traits::get_ref (p)); + + current (pointer_type (p)); + current (r); + } + else + { + const id_type& id (current_id ()); + + // First check the session. + // + pointer_type p ( + pointer_cache_traits<pointer_type>::find (database (), id)); + + if (!pointer_traits::null_ptr (p)) + current (p); + else + { + unrestricted_pointer_type up (object_traits::create ()); + + typename + pointer_cache_traits<unrestricted_pointer_type>::insert_guard ig ( + pointer_cache_traits<unrestricted_pointer_type>::insert ( + database (), id, up)); + + object_type& r (unrestricted_pointer_traits::get_ref (up)); + + current (pointer_type (up)); + current (r); + + ig.release (); + } + } } return current_; } + + // + // result_iterator + // + + template <typename T> + void result_iterator<T>:: + load (object_type& x) + { + if (res_->end ()) + return; + + if (!session::has_current ()) + res_->current (x); + else + { + const id_type& id (res_->current_id ()); + + typename reference_cache_traits<object_type>::insert_guard ig ( + reference_cache_traits<object_type>::insert ( + res_->database (), id, x)); + + res_->current (x); + ig.release (); + } + } } diff --git a/odb/session.cxx b/odb/session.cxx new file mode 100644 index 0000000..b34c1d7 --- /dev/null +++ b/odb/session.cxx @@ -0,0 +1,72 @@ +// file : odb/session.cxx +// author : Boris Kolpackov <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include <odb/exceptions.hxx> +#include <odb/session.hxx> + +#include <odb/details/tls.hxx> + +namespace odb +{ + using namespace details; + + static ODB_TLS_POINTER (session) current_session; + + session:: + session () + { + if (has_current ()) + throw already_in_session (); + + current (*this); + } + + session:: + ~session () + { + // If we are the current thread's session, reset it. + // + if (has_current () && ¤t () == this) + reset_current (); + } + + bool session:: + has_current () + { + return tls_get (current_session) != 0; + } + + session& session:: + current () + { + session* cur (tls_get (current_session)); + + if (cur == 0) + throw not_in_session (); + + return *cur; + } + + void session:: + current (session& s) + { + tls_set (current_session, &s); + } + + void session:: + reset_current () + { + session* s (0); + tls_set (current_session, s); + } + + // + // object_map_base + // + session::object_map_base:: + ~object_map_base () + { + } +} diff --git a/odb/session.hxx b/odb/session.hxx new file mode 100644 index 0000000..033934a --- /dev/null +++ b/odb/session.hxx @@ -0,0 +1,159 @@ +// file : odb/session.hxx +// author : Boris Kolpackov <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_SESSION_HXX +#define ODB_SESSION_HXX + +#include <odb/pre.hxx> + +#include <map> +#include <typeinfo> + +#include <odb/traits.hxx> +#include <odb/forward.hxx> + +#include <odb/details/shared-ptr.hxx> +#include <odb/details/type-info.hxx> + +#include <odb/details/export.hxx> + +namespace odb +{ + class LIBODB_EXPORT session + { + public: + typedef odb::database database_type; + + // Set the current thread's session to this session. If another + // session is already in effect, throw the already_in_session + // exception. + // + session (); + + // Reset the current thread's session if it is this session. + // + ~session (); + + // Current session. + // + public: + // Return true if there is a session in effect in the current + // thread. + // + static bool + has_current (); + + // Get current thread's session. Throw if no session is in effect. + // + static session& + current (); + + // Set current thread's session. + // + static void + current (session&); + + // Revert to the no session in effect state for the current thread. + // + static void + reset_current (); + + // Copying or assignment of sessions is not supported. + // + private: + session (const session&); + session& operator= (const session&); + + protected: + template <typename T> + struct object_pointers + { + typedef typename object_traits<T>::pointer_type pointer_type; + typedef typename object_traits<T>::const_pointer_type const_pointer_type; + + object_pointers (); + + void + set (const pointer_type&); + + void + set (const const_pointer_type&); + + void + get (pointer_type& p) const; + + void + get (const_pointer_type& cp) const; + + private: + pointer_type p_; + const_pointer_type cp_; + }; + + struct LIBODB_EXPORT object_map_base: details::shared_base + { + virtual + ~object_map_base (); + }; + + template <typename T> + struct object_map: + object_map_base, + std::map< typename object_traits<T>::id_type, object_pointers<T> > + { + }; + + // Object cache. + // + public: + template <typename T> + struct object_position + { + typedef typename object_traits<T>::object_type object_type; + typedef object_map<object_type> map; + typedef typename map::iterator iterator; + + object_position (): map_ (0) {} + object_position (map& m, const iterator& p): map_ (&m), pos_ (p) {} + + map* map_; + iterator pos_; + }; + + template <typename T> + object_position<T> + insert (database_type&, + const typename object_traits<T>::id_type&, + const typename object_traits<T>::pointer_type&); + + template <typename T> + typename object_traits<T>::pointer_type + find (database_type&, const typename object_traits<T>::id_type&) const; + + template <typename T> + void + erase (database_type&, const typename object_traits<T>::id_type&); + + template <typename T> + void + erase (const object_position<T>&); + + protected: + typedef std::map<const std::type_info*, + details::shared_ptr<object_map_base>, + details::type_info_comparator> type_map; + + typedef std::map<database_type*, type_map> database_map; + + database_map db_map_; + }; +} + +#include <odb/session.ixx> +#include <odb/session.txx> + +#include <odb/post.hxx> + +#endif // ODB_SESSION_HXX diff --git a/odb/session.ixx b/odb/session.ixx new file mode 100644 index 0000000..0be6bfa --- /dev/null +++ b/odb/session.ixx @@ -0,0 +1,65 @@ +// file : odb/session.ixx +// author : Boris Kolpackov <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include <odb/exceptions.hxx> + +namespace odb +{ + template <typename T> + inline void session:: + erase (const object_position<T>& p) + { + // @@ Empty maps are not cleaned up by this version of erase. + // + p.map_->erase (p.pos_); + } + + // + // object_pointers + // + template <typename T> + inline session::object_pointers<T>:: + object_pointers () : p_ (), cp_ () {} + + template <typename T> + inline void session::object_pointers<T>:: + set (const pointer_type& p) + { + p_ = p; + cp_ = const_pointer_type (); + } + + template <typename T> + inline void session::object_pointers<T>:: + set (const const_pointer_type& cp) + { + p_ = pointer_type (); + cp_ = cp; + } + + template <typename T> + inline void session::object_pointers<T>:: + get (pointer_type& p) const + { + if (!pointer_traits<pointer_type>::null_ptr (p_)) + p = p_; + else if (!pointer_traits<const_pointer_type>::null_ptr (cp_)) + throw const_object (); + else + p = pointer_type (); + } + + template <typename T> + inline void session::object_pointers<T>:: + get (const_pointer_type& cp) const + { + if (!pointer_traits<pointer_type>::null_ptr (p_)) + cp = const_pointer_type (p_); + else if (!pointer_traits<const_pointer_type>::null_ptr (cp_)) + cp = cp_; + else + cp = const_pointer_type (); + } +} diff --git a/odb/session.txx b/odb/session.txx new file mode 100644 index 0000000..1e3ba96 --- /dev/null +++ b/odb/session.txx @@ -0,0 +1,116 @@ +// file : odb/session.txx +// author : Boris Kolpackov <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include <odb/pointer-traits.hxx> + +namespace odb +{ + template <typename T> + typename session::object_position<T> session:: + insert (database_type& db, + const typename object_traits<T>::id_type& id, + const typename object_traits<T>::pointer_type& obj) + { + // T can be const T while object_type will always be T. + // + typedef typename object_traits<T>::object_type object_type; + typedef odb::object_traits<object_type> object_traits; + + typedef typename odb::object_traits<T>::pointer_type pointer_type; + typedef odb::pointer_traits<pointer_type> pointer_traits; + + type_map& tm (db_map_[&db]); + details::shared_ptr<object_map_base>& pom (tm[&typeid (object_type)]); + + if (!pom) + pom.reset (new (details::shared) object_map<object_type>); + + object_map<object_type>& om ( + static_cast<object_map<object_type>&> (*pom)); + + typename object_map<object_type>::value_type vt ( + id, object_pointers<object_type> ()); + vt.second.set (obj); + std::pair<typename object_map<object_type>::iterator, bool> r ( + om.insert (vt)); + + // In what situation may we possibly attempt to reinsert the object? + // For example, when the user loads the same object in to different + // instances (i.e., load into a pre-allocated object). In this case + // we should probably update our entries accordingly. + // + if (!r.second) + r.first->second.set (obj); + + return object_position<T> (om, r.first); + } + + template <typename T> + typename object_traits<T>::pointer_type session:: + find (database_type& db, const typename object_traits<T>::id_type& id) const + { + // T can be const T while object_type will always be T. + // + typedef typename object_traits<T>::object_type object_type; + typedef typename object_traits<T>::pointer_type pointer_type; + + database_map::const_iterator di (db_map_.find (&db)); + + if (di == db_map_.end ()) + return pointer_type (); + + const type_map& tm (di->second); + type_map::const_iterator ti (tm.find (&typeid (object_type))); + + if (ti == tm.end ()) + return pointer_type (); + + const object_map<object_type>& om ( + static_cast<const object_map<object_type>&> (*ti->second)); + typename object_map<object_type>::const_iterator oi (om.find (id)); + + if (oi == om.end ()) + return pointer_type (); + + pointer_type r; + oi->second.get (r); + return r; + } + + template <typename T> + void session:: + erase (database_type& db, const typename object_traits<T>::id_type& id) + { + // T can be const T while object_type will always be T. + // + typedef typename object_traits<T>::object_type object_type; + + database_map::iterator di (db_map_.find (&db)); + + if (di == db_map_.end ()) + return; + + type_map& tm (di->second); + type_map::iterator ti (tm.find (&typeid (object_type))); + + if (ti == tm.end ()) + return; + + object_map<object_type>& om ( + static_cast<object_map<object_type>&> (*ti->second)); + typename object_map<object_type>::iterator oi (om.find (id)); + + if (oi == om.end ()) + return; + + om.erase (oi); + + if (om.empty ()) + tm.erase (ti); + + if (tm.empty ()) + db_map_.erase (di); + } +} diff --git a/odb/traits.hxx b/odb/traits.hxx index bc25dcb..87fcec5 100644 --- a/odb/traits.hxx +++ b/odb/traits.hxx @@ -72,15 +72,74 @@ namespace odb }; template <typename T> - struct object_traits: access::object_traits<T>, + struct object_traits: + access::object_traits<T>, access::object_factory<T, typename access::object_traits<T>::pointer_type> { + // + // If a C++ compiler issues an error pointing to this struct and + // saying that it is incomplete, then you are most likely trying to + // perform a database operation on a C++ type that is not a persistent + // object. Or you forgot to include the corresponding -odb.hxx file. + // + + typedef + odb::pointer_traits<typename access::object_traits<T>::pointer_type> + pointer_traits; + typedef typename access::object_traits<T>::object_type object_type; typedef typename access::object_traits<T>::pointer_type pointer_type; + typedef typename pointer_traits::const_pointer_type const_pointer_type; + }; + // Specialization for const objects. It only defines the id, object, + // pointer, and const_pointer types with pointer and const_pointer + // being the same. The idea is to only use this specialization in the + // interfaces, with the implementations detecting this situation and + // using the non-const object_traits version. + // + template <typename T> + struct object_traits<const T> + { + private: typedef odb::pointer_traits<typename access::object_traits<T>::pointer_type> pointer_traits; + + public: + typedef typename access::object_traits<T>::id_type id_type; + typedef typename access::object_traits<T>::object_type object_type; + typedef typename pointer_traits::const_pointer_type const_pointer_type; + typedef const_pointer_type pointer_type; + }; + + // Specializations for pointer types to allow the C++ compiler to + // instantiate persist(), etc., signatures in class database. The + // overloads that use these specializations would never actually + // be selected by the compiler. + // + template <typename T> + struct object_traits<T*> + { + struct id_type {}; + }; + + template <typename T> + struct object_traits<T* const> + { + struct id_type {}; + }; + + template <typename T, template <typename> class P> + struct object_traits< P<T> > + { + struct id_type {}; + }; + + template <typename T, template <typename> class P> + struct object_traits< const P<T> > + { + struct id_type {}; }; template <typename T> |