diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2010-11-22 12:03:11 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2010-11-22 12:03:11 +0200 |
commit | 531c792dd4eecd246cc1ccebac812d6888464a78 (patch) | |
tree | 9bed050c98a2c407c68e808ae1f1d296a65c5fee | |
parent | 1cddfd09a7007f77fc243f178b1ca88ea4d0f4f6 (diff) |
Add session, database operations on pointers and const objects
Currently, session is just an object cache. The persist, update, and
erase database operations are overloaded to also work on object
pointers. All the database operations and the query facility now
support const objects.
New session-related exceptions: not_in_session, already_in_session,
const_object.
-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> |