aboutsummaryrefslogtreecommitdiff
path: root/odb/session.txx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2010-06-04 16:33:08 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2010-06-04 16:33:08 +0200
commit6f0d40508286afc8cdd72a0b5f807d5c2a589cfc (patch)
treef22a38560560664cae731c37537c2bb04bffdc2a /odb/session.txx
parente0f3efd9df3e7eefa06777717f23905022d1949e (diff)
Initial implementation
Diffstat (limited to 'odb/session.txx')
-rw-r--r--odb/session.txx320
1 files changed, 320 insertions, 0 deletions
diff --git a/odb/session.txx b/odb/session.txx
new file mode 100644
index 0000000..55155f7
--- /dev/null
+++ b/odb/session.txx
@@ -0,0 +1,320 @@
+// 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 <utility> // std::make_pair
+
+#include <odb/exceptions.hxx>
+#include <odb/transaction.hxx>
+
+#include <iostream> // @@ tmp
+
+namespace odb
+{
+ //
+ // session::object_proxy_impl
+ //
+
+ template <typename T>
+ void session::object_proxy_impl<T>::
+ persist ()
+ {
+ traits::insert (db_, ptr_ops::get_ref (obj_));
+ }
+
+ template <typename T>
+ void session::object_proxy_impl<T>::
+ update ()
+ {
+ traits::update (db_, ptr_ops::get_ref (obj_));
+ }
+
+ template <typename T>
+ void session::object_proxy_impl<T>::
+ erase ()
+ {
+ traits::erase (db_, traits::id (ptr_ops::get_ref (obj_)));
+ }
+
+ template <typename T>
+ void session::object_proxy_impl<T>::
+ register_id (id_map& idm, object_map::iterator i)
+ {
+ // For polymorphic types we should use the root of the hierarchy
+ // (presumably defined in traits) as a type id so that all types
+ // in this hierarchy end up in the same map. See also other places.
+ //
+ shared_ptr<type_map>& m (idm[&typeid (T)]);
+
+ if (!m)
+ m.reset (new (shared) type_map_impl<T>);
+
+ type_map_impl<T>& mi (static_cast<type_map_impl<T>&> (*m));
+
+ std::pair<typename type_map_impl<T>::iterator, bool> r (
+ mi.insert (std::make_pair (traits::id (ptr_ops::get_ref (obj_)), i)));
+
+ if (!r.second)
+ throw object_already_persistent ();
+ }
+
+ template <typename T>
+ void session::object_proxy_impl<T>::
+ unregister_id (id_map& idm)
+ {
+ shared_ptr<type_map>& m (idm[&typeid (T)]);
+
+ if (m)
+ {
+ type_map_impl<T>& mi (static_cast<type_map_impl<T>&> (*m));
+ mi.erase (traits::id (ptr_ops::get_ref (obj_)));
+ }
+ }
+
+ //
+ // session
+ //
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type session::
+ persist (database& db, P<T> p)
+ {
+ typedef object_traits<T> traits;
+ typedef typename traits::shared_ops ops;
+
+ // P<T> should be the same or convertible to
+ // object_traits<T>::shared_ptr.
+ //
+ const typename traits::shared_ptr& obj (p);
+
+ // For polymorphic types we need to cast the pointer to the
+ // root of the hierarchy.
+ //
+ void* ptr (ops::get_ptr (obj));
+ T& ref (ops::get_ref (obj));
+
+ // @@ What if the object that's already in the map is in the
+ // erased state?
+ //
+ if (object_map_.find (ptr) != object_map_.end ())
+ throw object_already_persistent ();
+
+ shared_ptr<object_proxy> pxy (new (shared) object_proxy_impl<T> (db, obj));
+
+ // If we are in a transaction, save this object immediately. This
+ // helps with two things: (1) assignment of auto-generated ids and
+ // (2) detection of duplicate objects.
+ //
+ if (transaction::has_current ())
+ {
+ // @@ What if the manually-assigned id is already in use by
+ // an object waiting to be deleted?
+ //
+ traits::insert (db, ref);
+ pxy->state_ = object_proxy::clean;
+ }
+ else
+ pxy->state_ = object_proxy::transient;
+
+ object_map::iterator i (
+ object_map_.insert (std::make_pair (ptr, pxy)).first);
+
+ // If this object has auto-assigned id and we haven't hit the db
+ // yet, then the id is "NULL" and we cannot insert this object
+ // into the id map.
+ //
+ if (pxy->state_ != object_proxy::transient ||
+ traits::id_source == ids_assigned)
+ {
+ try
+ {
+ pxy->register_id (id_map_, i);
+ }
+ catch (...)
+ {
+ object_map_.erase (i);
+ throw;
+ }
+ }
+
+ return traits::id (ref);
+ }
+
+ template <typename T>
+ typename object_traits<T>::shared_ptr session::
+ find (database& db, typename object_traits<T>::id_type const& id)
+ {
+ typedef object_traits<T> traits;
+ typedef typename traits::shared_ops ops;
+ typedef typename traits::shared_ptr obj_ptr;
+
+ // First see if we have this object in the maps.
+ //
+ shared_ptr<type_map>& m (id_map_[&typeid (T)]);
+
+ if (m)
+ {
+ typedef type_map_impl<T> map_impl;
+ map_impl& mi (static_cast<map_impl&> (*m));
+ typename map_impl::iterator i (mi.find (id));
+
+ if (i != mi.end ())
+ {
+ const object_map::iterator& j (i->second);
+
+ if (j->second->state_ != object_proxy::erased)
+ {
+ object_proxy_impl<T>& pxy (
+ static_cast<object_proxy_impl<T>&> (*j->second));
+ return pxy.obj_;
+ }
+ else
+ return obj_ptr ();
+ }
+ }
+
+ // If we are in transaction then hit the database. Otherwise, the
+ // object is not found.
+ //
+ if (!transaction::has_current ())
+ return obj_ptr ();
+
+ obj_ptr obj (traits::find (db, id));
+
+ if (ops::null_ptr (obj))
+ return obj;
+
+ // Add this object to our maps.
+ //
+ void* ptr (ops::get_ptr (obj));
+ shared_ptr<object_proxy> pxy (new (shared) object_proxy_impl<T> (db, obj));
+ pxy->state_ = object_proxy::clean;
+
+ object_map::iterator i (
+ object_map_.insert (
+ std::make_pair (ptr, pxy)).first);
+
+ try
+ {
+ if (!m)
+ m.reset (new (shared) type_map_impl<T>);
+
+ type_map_impl<T>& mi (static_cast<type_map_impl<T>&> (*m));
+ mi.insert (std::make_pair (id, i));
+ }
+ catch (...)
+ {
+ object_map_.erase (i);
+ throw;
+ }
+
+ return obj;
+ }
+
+ template <typename T, template <typename> class P>
+ void session::
+ erase (database& db, P<T> p)
+ {
+ typedef object_traits<T> traits;
+ typedef typename traits::shared_ops ops;
+
+ // P<T> should be the same or convertible to
+ // object_traits<T>::shared_ptr.
+ //
+ const typename traits::shared_ptr& obj (p);
+
+ // For polymorphic types we need to cast the pointer to the
+ // root of the hierarchy.
+ //
+ void* ptr (ops::get_ptr (obj));
+ T& ref (ops::get_ref (obj));
+
+ object_map::iterator i (object_map_.find (ptr));
+
+ if (object_map_.find (ptr) == object_map_.end ())
+ throw object_not_persistent ();
+
+ object_proxy& pxy (*i->second);
+
+ switch (pxy.state_)
+ {
+ case object_proxy::transient:
+ {
+ // If this object is still transient then all we need to do is
+ // purge it from the maps.
+ //
+
+ // See if we are registered in the id map.
+ //
+ if (traits::id_source == ids_assigned)
+ pxy.unregister_id (id_map_);
+
+ object_map_.erase (i);
+ break;
+ }
+ case object_proxy::clean:
+ case object_proxy::dirty:
+ {
+ // Ideally we would need to store the object id as well as the
+ // version (optimistic concurrency) since the user is free to
+ // modify the object state.
+ //
+ pxy.state_ = object_proxy::erased;
+ break;
+ }
+ case object_proxy::erased:
+ {
+ // Already erased. Throw to be consistent with the transient
+ // case.
+ //
+ throw object_not_persistent ();
+ }
+ }
+ }
+
+ template <typename T, template <typename> class P>
+ void session::
+ modified (P<T> p)
+ {
+ typedef object_traits<T> traits;
+ typedef typename traits::shared_ops ops;
+
+ // P<T> should be the same or convertible to
+ // object_traits<T>::shared_ptr.
+ //
+ const typename traits::shared_ptr& obj (p);
+
+ // For polymorphic types we need to cast the pointer to the
+ // root of the hierarchy.
+ //
+ void* ptr (ops::get_ptr (obj));
+
+ object_map::iterator i (object_map_.find (ptr));
+
+ if (object_map_.find (ptr) == object_map_.end ())
+ throw object_not_persistent ();
+
+ object_proxy& pxy (*i->second);
+
+ switch (pxy.state_)
+ {
+ case object_proxy::transient:
+ case object_proxy::dirty:
+ {
+ // Nothing to do here.
+ //
+ break;
+ }
+ case object_proxy::clean:
+ {
+ pxy.state_ = object_proxy::dirty;
+ break;
+ }
+ case object_proxy::erased:
+ {
+ throw object_not_persistent ();
+ }
+ }
+ }
+}