diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2010-06-04 16:33:08 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2010-06-04 16:33:08 +0200 |
commit | 6f0d40508286afc8cdd72a0b5f807d5c2a589cfc (patch) | |
tree | f22a38560560664cae731c37537c2bb04bffdc2a | |
parent | e0f3efd9df3e7eefa06777717f23905022d1949e (diff) |
Initial implementation
32 files changed, 2273 insertions, 6 deletions
diff --git a/makefile b/makefile new file mode 100644 index 0000000..32b7548 --- /dev/null +++ b/makefile @@ -0,0 +1,34 @@ +# file : makefile +# author : Boris Kolpackov <boris@codesynthesis.com> +# copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +# license : GNU GPL v2; see accompanying LICENSE file + +include $(dir $(lastword $(MAKEFILE_LIST)))build/bootstrap.make + +default := $(out_base)/ +test := $(out_base)/.test +install := $(out_base)/.install +clean := $(out_base)/.clean + +# Build. +# +$(default): $(out_base)/odb/ #$(out_base)/tests/ + +# Test. +# +$(test): $(out_base)/tests/.test + +# Install. +# +$(install): $(out_base)/odb/.install + $(call install-data,$(src_base)/LICENSE,$(install_doc_dir)/libodb/LICENSE) + $(call install-data,$(src_base)/README,$(install_doc_dir)/libodb/README) + +# Clean. +# +$(clean): $(out_base)/odb/.clean $(out_base)/tests/.clean + +$(call include,$(bld_root)/install.make) + +$(call import,$(src_base)/odb/makefile) +#$(call import,$(src_base)/tests/makefile) diff --git a/odb/core.hxx b/odb/core.hxx index 227dea7..ea0ed05 100644 --- a/odb/core.hxx +++ b/odb/core.hxx @@ -13,11 +13,6 @@ # define PRAGMA_ODB(x) #endif -namespace odb -{ - class image - { - }; -} +#include <odb/forward.hxx> #endif // ODB_CORE_HXX diff --git a/odb/database.cxx b/odb/database.cxx new file mode 100644 index 0000000..aa75dae --- /dev/null +++ b/odb/database.cxx @@ -0,0 +1,14 @@ +// file : odb/database.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/database.hxx> + +namespace odb +{ + database:: + ~database () + { + } +} diff --git a/odb/database.hxx b/odb/database.hxx new file mode 100644 index 0000000..2a904a4 --- /dev/null +++ b/odb/database.hxx @@ -0,0 +1,71 @@ +// file : odb/database.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_DATABASE_HXX +#define ODB_DATABASE_HXX + +#include <odb/traits.hxx> +#include <odb/forward.hxx> + +namespace odb +{ + class transaction_impl; + + class database + { + public: + virtual + ~database (); + + template <typename T, template <typename> class P> + typename object_traits<T>::id_type + persist (P<T> obj); + + template <typename T> + typename object_traits<T>::shared_ptr + load (typename object_traits<T>::id_type const&); + + template <typename T> + typename object_traits<T>::shared_ptr + find (typename object_traits<T>::id_type const&); + + template <typename T, template <typename> class P> + void + erase (P<T> obj); + + template <typename T, template <typename> class P> + void + modified (P<T> obj); + + // Transaction API. + // + public: + // Start a transaction. If an existing session can be obtained via + // session::current(), the transaction is run as part of that session. + // Otherwise a new session is created and will be automatically flushed + // and destroyed when transaction ends. + // + virtual transaction_impl* + begin_transaction () = 0; + + // Start a transaction as part of an existing session. The session + // is not automatically flushed or destroyed when transaction ends. + // + virtual transaction_impl* + begin_transaction (session&) = 0; + + protected: + database (); + + private: + database (const database&); + database& operator= (const database&); + }; +} + +#include <odb/database.ixx> +#include <odb/database.txx> + +#endif // ODB_DATABASE_HXX diff --git a/odb/database.ixx b/odb/database.ixx new file mode 100644 index 0000000..c8fe08c --- /dev/null +++ b/odb/database.ixx @@ -0,0 +1,12 @@ +// file : odb/database.ixx +// author : Boris Kolpackov <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +namespace odb +{ + inline database:: + database () + { + } +} diff --git a/odb/database.txx b/odb/database.txx new file mode 100644 index 0000000..c507030 --- /dev/null +++ b/odb/database.txx @@ -0,0 +1,73 @@ +// file : odb/database.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/exceptions.hxx> +#include <odb/session.hxx> +#include <odb/transaction.hxx> + +namespace odb +{ + // @@ Should I make these inline? + // + + template <typename T, template <typename> class P> + typename object_traits<T>::id_type database:: + persist (P<T> p) + { + // P<T> should be the same or convertible to + // object_traits<T>::shared_ptr. + // + const typename object_traits<T>::shared_ptr& obj (p); + + session& s (transaction::current ().session ()); + return s.persist<T> (*this, obj); + } + + template <typename T> + typename object_traits<T>::shared_ptr database:: + load (typename object_traits<T>::id_type const& id) + { + typename object_traits<T>::shared_ptr r (find<T> (id)); + + if (object_traits<T>::shared_ops::null_ptr (r)) + throw object_not_persistent (); + + return r; + } + + template <typename T> + typename object_traits<T>::shared_ptr database:: + find (typename object_traits<T>::id_type const& id) + { + session& s (transaction::current ().session ()); + return s.find<T> (*this, id); + } + + template <typename T, template <typename> class P> + void database:: + erase (P<T> p) + { + // P<T> should be the same or convertible to + // object_traits<T>::shared_ptr. + // + const typename object_traits<T>::shared_ptr& obj (p); + + session& s (transaction::current ().session ()); + return s.erase (*this, obj); + } + + template <typename T, template <typename> class P> + void database:: + modified (P<T> p) + { + // P<T> should be the same or convertible to + // object_traits<T>::shared_ptr. + // + const typename object_traits<T>::shared_ptr& obj (p); + + session& s (transaction::current ().session ()); + return s.modified (obj); + } +} diff --git a/odb/exception.cxx b/odb/exception.cxx new file mode 100644 index 0000000..52d8370 --- /dev/null +++ b/odb/exception.cxx @@ -0,0 +1,17 @@ +// file : odb/exception.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 <typeinfo> + +#include <odb/exception.hxx> + +namespace odb +{ + char const* exception:: + what () const throw () + { + return typeid (*this).name (); + } +} diff --git a/odb/exception.hxx b/odb/exception.hxx new file mode 100644 index 0000000..ff3fc63 --- /dev/null +++ b/odb/exception.hxx @@ -0,0 +1,22 @@ +// file : odb/exception.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_EXCEPTION_HXX +#define ODB_EXCEPTION_HXX + +#include <exception> + +namespace odb +{ + struct exception: std::exception + { + // By default return the exception type name ( typeid (*this).name () ). + // + virtual char const* + what () const throw (); + }; +} + +#endif // ODB_EXCEPTION_HXX diff --git a/odb/exceptions.cxx b/odb/exceptions.cxx new file mode 100644 index 0000000..d86657c --- /dev/null +++ b/odb/exceptions.cxx @@ -0,0 +1,10 @@ +// file : odb/exceptions.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> + +namespace odb +{ +} diff --git a/odb/exceptions.hxx b/odb/exceptions.hxx new file mode 100644 index 0000000..4b5909b --- /dev/null +++ b/odb/exceptions.hxx @@ -0,0 +1,52 @@ +// file : odb/exceptions.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_EXCEPTIONS_HXX +#define ODB_EXCEPTIONS_HXX + +#include <odb/exception.hxx> + +namespace odb +{ + // nested_transaction + // + struct already_in_transaction: odb::exception + { + }; + + // no_transaction + // + struct not_in_transaction: odb::exception + { + }; + + // finilized_transaction + // + struct transaction_already_finilized: odb::exception + { + }; + + struct already_in_session: odb::exception + { + }; + + struct not_in_session: odb::exception + { + }; + + struct object_not_persistent: odb::exception + { + }; + + struct object_already_persistent: odb::exception + { + }; + + struct database_exception: odb::exception + { + }; +} + +#endif // ODB_EXCEPTIONS_HXX diff --git a/odb/forward.hxx b/odb/forward.hxx new file mode 100644 index 0000000..b59034f --- /dev/null +++ b/odb/forward.hxx @@ -0,0 +1,38 @@ +// file : odb/forward.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_FORWARD_HXX +#define ODB_FORWARD_HXX + +namespace odb +{ + class session; + class database; + class transaction; + + template <typename T> + class shared_ptr; + + class access + { + public: + template <typename T> + class value_traits; + + template <typename T> + class object_traits; + + template <typename T> + class object_memory; + + template <typename T> + class object_factory; + + template <typename P> + class shared_factory; + }; +} + +#endif // ODB_FORWARD_HXX diff --git a/odb/makefile b/odb/makefile new file mode 100644 index 0000000..71c719a --- /dev/null +++ b/odb/makefile @@ -0,0 +1,60 @@ +# file : odb/makefile +# author : Boris Kolpackov <boris@codesynthesis.com> +# copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +# license : GNU GPL v2; see accompanying LICENSE file + +include $(dir $(lastword $(MAKEFILE_LIST)))../build/bootstrap.make + +cxx_tun := \ +exception.cxx \ +exceptions.cxx \ +shared-ptr/base.cxx \ +session.cxx \ +database.cxx \ +transaction.cxx + +cxx_obj := $(addprefix $(out_base)/,$(cxx_tun:.cxx=.o)) +cxx_od := $(cxx_obj:.o=.o.d) + +odb.l := $(out_base)/odb.l +odb.l.cpp-options := $(out_base)/odb.l.cpp-options + +default := $(out_base)/ +install := $(out_base)/.install +clean := $(out_base)/.clean + + +# Build. +# +$(odb.l): $(cxx_obj) + +$(cxx_obj) $(cxx_od): $(odb.l.cpp-options) +$(odb.l.cpp-options): value := -I$(src_root) + +$(call include-dep,$(cxx_od)) + + +# Convenience alias for default target. +# +$(out_base)/: $(odb.l) + +# Install. +# +$(install): $(odb.l) + $(call install-lib,$<,$(install_lib_dir)/$(ld_lib_prefix)odb$(ld_lib_suffix)) + $(call install-dir,$(src_base),$(install_inc_dir)/odb,\ +'(' -name '*.hxx' -o -name '*.ixx' -o -name '*.txx' ')') + +# Clean. +# +$(clean): $(odb.l).o.clean \ + $(odb.l.cpp-options).clean \ + $(addsuffix .cxx.clean,$(cxx_obj)) \ + $(addsuffix .cxx.clean,$(cxx_od)) + + +# How to. +# +$(call include,$(bld_root)/cxx/o-l.make) +$(call include,$(bld_root)/cxx/cxx-o.make) +$(call include,$(bld_root)/cxx/cxx-d.make) diff --git a/odb/meta/answer.hxx b/odb/meta/answer.hxx new file mode 100644 index 0000000..80a25e0 --- /dev/null +++ b/odb/meta/answer.hxx @@ -0,0 +1,25 @@ +// file : odb/meta/answer.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_META_ANSWER_HXX +#define ODB_META_ANSWER_HXX + +namespace odb +{ + namespace meta + { + struct yes + { + char filling; + }; + + struct no + { + char filling[2]; + }; + } +} + +#endif // ODB_META_ANSWER_HXX diff --git a/odb/meta/class-p.hxx b/odb/meta/class-p.hxx new file mode 100644 index 0000000..e3d15c9 --- /dev/null +++ b/odb/meta/class-p.hxx @@ -0,0 +1,28 @@ +// file : odb/meta/class-p.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_META_CLASS_HXX +#define ODB_META_CLASS_HXX + +#include <odb/meta/answer.hxx> + +namespace odb +{ + namespace meta + { + // g++ cannot have these inside class_p. + // + template <typename Y> no class_p_test (...); + template <typename Y> yes class_p_test (void (Y::*) ()); + + template <typename X> + struct class_p + { + static bool const r = sizeof (class_p_test<X> (0)) == sizeof (yes); + }; + } +} + +#endif // ODB_META_CLASS_HXX diff --git a/odb/meta/polymorphic-p.hxx b/odb/meta/polymorphic-p.hxx new file mode 100644 index 0000000..571ef52 --- /dev/null +++ b/odb/meta/polymorphic-p.hxx @@ -0,0 +1,51 @@ +// file : odb/meta/polymorphic-p.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_META_POLYMORPHIC_HXX +#define ODB_META_POLYMORPHIC_HXX + +#include <odb/meta/class-p.hxx> +#include <odb/meta/remove-cv.hxx> + +namespace odb +{ + namespace meta + { + template <typename CVX> + struct polymorphic_p + { + typedef typename remove_cv<CVX>::r X; + + template <typename Y, bool C> + struct impl + { + static const bool r = false; + }; + + template <typename Y> + struct impl<Y, true> + { + struct t1: Y + { + t1 (); + }; + + struct t2: Y + { + t2 (); + + virtual + ~t2 () throw (); + }; + + static const bool r = sizeof (t1) == sizeof (t2); + }; + + static const bool r = impl<X, class_p<X>::r>::r; + }; + } +} + +#endif // ODB_META_POLYMORPHIC_HXX diff --git a/odb/meta/remove-c.hxx b/odb/meta/remove-c.hxx new file mode 100644 index 0000000..db724b3 --- /dev/null +++ b/odb/meta/remove-c.hxx @@ -0,0 +1,27 @@ +// file : odb/meta/remove-c.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_META_REMOVE_C_HXX +#define ODB_META_REMOVE_C_HXX + +namespace odb +{ + namespace meta + { + template <typename X> + struct remove_c + { + typedef X r; + }; + + template <typename X> + struct remove_c<X const> + { + typedef X r; + }; + } +} + +#endif // ODB_META_REMOVE_C_HXX diff --git a/odb/meta/remove-cv.hxx b/odb/meta/remove-cv.hxx new file mode 100644 index 0000000..0805a9d --- /dev/null +++ b/odb/meta/remove-cv.hxx @@ -0,0 +1,24 @@ +// file : odb/meta/remove-cv.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_META_REMOVE_CV_HXX +#define ODB_META_REMOVE_CV_HXX + +#include <odb/meta/remove-c.hxx> +#include <odb/meta/remove-v.hxx> + +namespace odb +{ + namespace meta + { + template <typename X> + struct remove_cv + { + typedef typename remove_v<typename remove_c<X>::r>::r r; + }; + } +} + +#endif // ODB_META_REMOVE_CV_HXX diff --git a/odb/meta/remove-p.hxx b/odb/meta/remove-p.hxx new file mode 100644 index 0000000..053c860 --- /dev/null +++ b/odb/meta/remove-p.hxx @@ -0,0 +1,27 @@ +// file : odb/meta/remove-p.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_META_REMOVE_P_HXX +#define ODB_META_REMOVE_P_HXX + +namespace odb +{ + namespace meta + { + template <typename X> + struct remove_p + { + typedef X r; + }; + + template <typename X> + struct remove_p<X*> + { + typedef X r; + }; + } +} + +#endif // ODB_META_REMOVE_P_HXX diff --git a/odb/meta/remove-v.hxx b/odb/meta/remove-v.hxx new file mode 100644 index 0000000..9e37a6c --- /dev/null +++ b/odb/meta/remove-v.hxx @@ -0,0 +1,27 @@ +// file : odb/meta/remove-v.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_META_REMOVE_V_HXX +#define ODB_META_REMOVE_V_HXX + +namespace odb +{ + namespace meta + { + template <typename X> + struct remove_v + { + typedef X r; + }; + + template <typename X> + struct remove_v<X volatile> + { + typedef X r; + }; + } +} + +#endif // ODB_META_REMOVE_V_HXX diff --git a/odb/session.cxx b/odb/session.cxx new file mode 100644 index 0000000..9ee0104 --- /dev/null +++ b/odb/session.cxx @@ -0,0 +1,120 @@ +// 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 <utility> // std::make_pair + +#include <odb/session.hxx> +#include <odb/transaction.hxx> + +namespace odb +{ + session::type_map:: + ~type_map () + { + } + + session::object_proxy:: + ~object_proxy () + { + } + + // + // session + // + + static session* current_session = 0; + + session:: + session () + { + if (current_session != 0) + throw already_in_session (); + + current_session = this; + } + + bool session:: + has_current () + { + return current_session != 0; + } + + session& session:: + current () + { + if (current_session == 0) + throw not_in_session (); + + return *current_session; + } + + void session:: + current (session& s) + { + current_session = &s; + } + + void session:: + reset_current () + { + current_session = 0; + } + + void session:: + flush () + { + if (!transaction::has_current ()) + throw not_in_transaction (); + + // @@ Order of insertion and deletion can be important (triggers, + // id assignment, constraints etc). + // + + for (object_map::iterator i (object_map_.begin ()), + e (object_map_.end ()); i != e;) + { + object_proxy& pxy (*i->second); + + switch (pxy.state_) + { + case object_proxy::transient: + { + pxy.persist (); + + // If the id is auto-assigned, then we only get it now, so + // register with the id map. + // + if (pxy.id_source_ != ids_assigned) + pxy.register_id (id_map_, i); + + pxy.state_ = object_proxy::clean; + ++i; + break; + } + case object_proxy::dirty: + { + pxy.update (); + pxy.state_ = object_proxy::clean; + ++i; + break; + } + case object_proxy::erased: + { + pxy.erase (); + pxy.unregister_id (id_map_); + object_map_.erase (i++); + break; + } + case object_proxy::clean: + { + // Nothing to do for this case. + // + ++i; + break; + } + } + } + } +} diff --git a/odb/session.hxx b/odb/session.hxx new file mode 100644 index 0000000..90df00d --- /dev/null +++ b/odb/session.hxx @@ -0,0 +1,190 @@ +// 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 <map> +#include <typeinfo> + +#include <odb/forward.hxx> +#include <odb/traits.hxx> + +namespace odb +{ + class session + { + public: + session (); + ~session () + { + reset_current (); + } + + private: + session (const session&); + session& operator= (const session&); + + public: + template <typename T, template <typename> class P> + typename object_traits<T>::id_type + persist (database&, P<T> obj); + + template <typename T> + typename object_traits<T>::shared_ptr + find (database&, typename object_traits<T>::id_type const&); + + template <typename T, template <typename> class P> + void + erase (database&, P<T> obj); + + template <typename T, template <typename> class P> + void + modified (P<T> obj); + + void + flush (); + + public: + // Return true if there is a session in effect. + // + static bool + has_current (); + + // Get current thread's session. Throw if no session in effect. + // + static session& + current (); + + // Set current thread's session. + // + static void + current (session&); + + // Revert to the no session in effect state. + // + static void + reset_current (); + + private: + struct object_proxy; + typedef std::map< void*, shared_ptr<object_proxy> > object_map; + + struct type_map + { + virtual + ~type_map () = 0; + }; + + template <typename T> + struct type_map_impl: + type_map, + std::map<typename object_traits<T>::id_type, object_map::iterator> + { + }; + + struct type_info_comparator + { + bool + operator() (const std::type_info* x, const std::type_info* y) const + { + // XL C++ on AIX has buggy type_info::before() in that + // it returns true for two different type_info objects + // that happened to be for the same type. + // +#if defined(__xlC__) && defined(_AIX) + return *x != *y && x->before (*y); +#else + return x->before (*y); +#endif + } + }; + + typedef + std::map<const std::type_info*, shared_ptr<type_map>, type_info_comparator> + id_map; + + // + // + struct object_proxy + { + virtual + ~object_proxy () = 0; + + object_proxy (database& db, id_source is) + : id_source_ (is), db_ (db) + { + } + + enum state + { + transient, /* Persisted out of transaction. */ + clean, + dirty, /* To be updated on flush. */ + erased /* To be deleted on flush. */ + }; + + public: + virtual void + persist () = 0; + + virtual void + update () = 0; + + virtual void + erase () = 0; + + virtual void + register_id (id_map&, object_map::iterator) = 0; + + virtual void + unregister_id (id_map&) = 0; + + public: + state state_; + id_source id_source_; + database& db_; + }; + + template <typename T> + struct object_proxy_impl: object_proxy + { + typedef object_traits<T> traits; + typedef typename traits::shared_ptr obj_ptr; + typedef typename traits::shared_ops ptr_ops; + + object_proxy_impl (database& db, obj_ptr obj) + : object_proxy (db, traits::id_source), obj_ (obj) + { + } + + public: + virtual void + persist (); + + virtual void + update (); + + virtual void + erase (); + + virtual void + register_id (id_map&, object_map::iterator); + + virtual void + unregister_id (id_map&); + + public: + obj_ptr obj_; + }; + + object_map object_map_; + id_map id_map_; + }; +} + +#include <odb/session.txx> + +#endif // ODB_SESSION_HXX 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 (); + } + } + } +} diff --git a/odb/shared-ptr-traits.hxx b/odb/shared-ptr-traits.hxx new file mode 100644 index 0000000..a1d5146 --- /dev/null +++ b/odb/shared-ptr-traits.hxx @@ -0,0 +1,119 @@ +// file : odb/shared-ptr-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_SHARED_PTR_TRAITS_HXX +#define ODB_SHARED_PTR_TRAITS_HXX + +#include <new> // operators new/delete +#include <cstddef> // std::size_t + +#include <odb/shared-ptr.hxx> + +namespace odb +{ + template <typename P> + class shared_ptr_traits; + + // Default implementation that should work for any sensible smart + // pointer with one template argument (object type). The only + // assumptions that we make are the availability of operator-> and + // operator*, and that the former does not throw if the pointer is + // NULL. + // + template <typename T, template <typename> class P> + class shared_ptr_traits< P<T> > + { + public: + typedef T type; + typedef P<T> shared_ptr; + + // Return underlying pointer, including NULL. + // + static type* + get_ptr (const shared_ptr& p) + { + return p.operator-> (); + } + + // Return reference to the pointed-to object. + // + static type& + get_ref (const shared_ptr& p) + { + return *p; + } + + // Return true if the pointer is NULL. + // + static bool + null_ptr (const shared_ptr& p) + { + return get_ptr () == 0; + } + + public: + // Allocate memory for a shared object. + // + static void* + allocate (std::size_t n) + { + return operator new (n); + } + + // Free memory allocated for a shared object. This functions is + // only called if the constructor of the object being created + // fails. Otherwise, shared_ptr is used to delete the object + // and free the memory. This behavior is identical to the one + // used by operator delete overloading. + // + static void + free (void* p) + { + operator delete (p); + } + }; + + // Specialization for odb::shared_ptr. + // + template <typename T> + class shared_ptr_traits< shared_ptr<T> > + { + public: + typedef T type; + typedef odb::shared_ptr<T> shared_ptr; + + static type* + get_ptr (const shared_ptr& p) + { + return p.get (); + } + + static type& + get_ref (const shared_ptr& p) + { + return *p; + } + + static bool + null_ptr (const shared_ptr& p) + { + return !p; + } + + static void* + allocate (std::size_t n) + { + return operator new (n, shared); + } + + static void + free (void* p) + { + operator delete (p, shared); + } + }; +} + +#endif // ODB_SHARED_PTR_TRAITS_HXX diff --git a/odb/shared-ptr.hxx b/odb/shared-ptr.hxx new file mode 100644 index 0000000..ee56792 --- /dev/null +++ b/odb/shared-ptr.hxx @@ -0,0 +1,145 @@ +// file : odb/shared-ptr.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_SHARED_PTR_HXX +#define ODB_SHARED_PTR_HXX + +#include <odb/shared-ptr/base.hxx> + +namespace odb +{ + template <typename X> + class shared_ptr: bits::counter_ops<typename bits::counter_type<X>::r, X> + { + typedef bits::counter_ops<typename bits::counter_type<X>::r, X> base; + + public: + ~shared_ptr () + { + if (x_ != 0) + base::dec (x_); + } + + explicit + shared_ptr (X* x = 0) + : base (x), x_ (x) + { + } + + shared_ptr (shared_ptr const& x) + : base (x), x_ (x.x_) + { + if (x_ != 0) + base::inc (x_); + } + + template <typename Y> + shared_ptr (shared_ptr<Y> const& x) + : base (x), x_ (x.x_) + { + if (x_ != 0) + base::inc (x_); + } + + shared_ptr& + operator= (shared_ptr const& x) + { + if (x_ != x.x_) + { + if (x_ != 0) + base::dec (x_); + + static_cast<base&> (*this) = x; + x_ = x.x_; + + if (x_ != 0) + base::inc (x_); + } + + return *this; + } + + template <typename Y> + shared_ptr& + operator= (shared_ptr<Y> const& x) + { + if (x_ != x.x_) + { + if (x_ != 0) + base::dec (x_); + + static_cast<base&> (*this) = x; + x_ = x.x_; + + if (x_ != 0) + base::inc (x_); + } + + return *this; + } + + public: + X* + operator-> () const + { + return x_; + } + + X& + operator* () const + { + return *x_; + } + + // Conversion to bool. + // + typedef void (shared_ptr::*boolean_convertible)(); + void true_value () {}; + + operator boolean_convertible () const + { + return x_ ? &shared_ptr<X>::true_value : 0; + } + + public: + X* + get () const + { + return x_; + } + + X* + release () + { + X* r (x_); + x_ = 0; + return r; + } + + void + reset (X* x) + { + if (x_ != 0) + base::dec (x_); + + base::reset (x); + x_ = x; + } + + std::size_t + count () const + { + return x_ != 0 ? base::count (x_) : 0; + } + + private: + template <typename> + friend class shared_ptr; + + X* x_; + }; +} + +#endif // ODB_SHARED_PTR_HXX diff --git a/odb/shared-ptr/base.cxx b/odb/shared-ptr/base.cxx new file mode 100644 index 0000000..11fa1c2 --- /dev/null +++ b/odb/shared-ptr/base.cxx @@ -0,0 +1,63 @@ +// file : odb/shared-ptr/base.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/shared-ptr/base.hxx> + +using std::size_t; + +// +// +odb::share shared = odb::share (1); +odb::share exclusive = odb::share (2); + +// +// +namespace odb +{ + char const* not_shared:: + what () const throw () + { + return "object is not shared"; + } +} + +// +// +void* +operator new (size_t n, odb::share s) throw (std::bad_alloc) +{ + if (s == shared) + { + // Here we need to make sure we don't break the alignment of the + // returned block. For that we need to know the maximum alignment + // of this platform. Twice the pointer size is a good guess for + // most platforms. + // + size_t* p = static_cast<size_t*> (operator new (n + 2 * sizeof (size_t))); + *p++ = 1; // Initial count. + *p++ = 0xDEADBEEF; // Signature. + return p; + } + else + return operator new (n); + +} + +void +operator delete (void* p, odb::share s) throw () +{ + // This version of operator delete is only called when the c-tor + // fails. In this case there is no object and we can just free the + // memory. + // + if (s == shared) + { + size_t* sp = static_cast<size_t*> (p); + sp -= 2; + operator delete (sp); + } + else + operator delete (p); +} diff --git a/odb/shared-ptr/base.hxx b/odb/shared-ptr/base.hxx new file mode 100644 index 0000000..8c1b62b --- /dev/null +++ b/odb/shared-ptr/base.hxx @@ -0,0 +1,91 @@ +// file : odb/shared-ptr/base.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_SHARED_PTR_BASE_HXX +#define ODB_SHARED_PTR_BASE_HXX + +#include <new> +#include <cstddef> // std::size_t + +#include <odb/exception.hxx> + +namespace odb +{ + struct share + { + explicit + share (char id); + + bool + operator== (share) const; + + private: + char id_; + }; +} + +extern odb::share shared; +extern odb::share exclusive; + +void* +operator new (std::size_t, odb::share) throw (std::bad_alloc); + +void +operator delete (void*, odb::share) throw (); + +namespace odb +{ + struct not_shared: exception + { + virtual char const* + what () const throw (); + }; + + struct shared_base + { + shared_base (); + shared_base (shared_base const&); + shared_base& + operator= (shared_base const&); + + void + _inc_ref (); + + bool + _dec_ref (); + + std::size_t + _ref_count () const; + + void* + operator new (std::size_t, share) throw (std::bad_alloc); + + void + operator delete (void*, share) throw (); + + void + operator delete (void*) throw (); + + protected: + std::size_t counter_; + }; + + template <typename X> + inline X* + inc_ref (X*); + + template <typename X> + inline void + dec_ref (X*); + + template <typename X> + inline std::size_t + ref_count (X const*); +} + +#include <odb/shared-ptr/base.ixx> +#include <odb/shared-ptr/base.txx> + +#endif // ODB_SHARED_PTR_BASE_HXX diff --git a/odb/shared-ptr/base.ixx b/odb/shared-ptr/base.ixx new file mode 100644 index 0000000..992e156 --- /dev/null +++ b/odb/shared-ptr/base.ixx @@ -0,0 +1,79 @@ +// file : odb/shared-ptr/base.ixx +// author : Boris Kolpackov <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +namespace odb +{ + // share + // + + inline share:: + share (char id) + : id_ (id) + { + } + + inline bool share:: + operator== (share x) const + { + return id_ == x.id_; + } + + // shared_base + // + + inline shared_base:: + shared_base () + : counter_ (1) + { + } + + inline shared_base:: + shared_base (shared_base const&) + : counter_ (1) + { + } + + inline shared_base& shared_base:: + operator= (shared_base const&) + { + return *this; + } + + inline void shared_base:: + _inc_ref () + { + counter_++; + } + + inline bool shared_base:: + _dec_ref () + { + return --counter_ == 0; + } + + inline std::size_t shared_base:: + _ref_count () const + { + return counter_; + } + + inline void* shared_base:: + operator new (std::size_t n, share) throw (std::bad_alloc) + { + return ::operator new (n); + } + + inline void shared_base:: + operator delete (void* p, share) throw () + { + ::operator delete (p); + } + + inline void shared_base:: + operator delete (void* p) throw () + { + ::operator delete (p); + } +} diff --git a/odb/shared-ptr/base.txx b/odb/shared-ptr/base.txx new file mode 100644 index 0000000..a8d5f26 --- /dev/null +++ b/odb/shared-ptr/base.txx @@ -0,0 +1,183 @@ +// file : odb/shared-ptr/base.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/meta/answer.hxx> +#include <odb/meta/polymorphic-p.hxx> + +namespace odb +{ + namespace bits + { + // Support for locating the counter in the memory block. + // + template <typename X, bool poly = meta::polymorphic_p<X>::r> + struct locator; + + template <typename X> + struct locator<X, false> + { + static std::size_t* + counter (X* x) + { + std::size_t* p (reinterpret_cast<std::size_t*> (x)); + + if (*(--p) != 0xDEADBEEF) + throw not_shared (); + + return --p; + } + }; + + template <typename X> + struct locator<X, true> + { + static std::size_t* + counter (X* x) + { + std::size_t* p ( + static_cast<std::size_t*> ( + dynamic_cast<void*> (x))); + + if (*(--p) != 0xDEADBEEF) + throw not_shared (); + + return --p; + } + }; + + template <typename X> + std::size_t* + counter (X const* p) + { + return bits::locator<X>::counter (const_cast<X*> (p)); + } + + // Counter type and operations. + // + meta::no test (...); + meta::yes test (shared_base*); + + template <typename X, + std::size_t A = sizeof (bits::test (reinterpret_cast<X*> (0)))> + struct counter_type; + + template <typename X> + struct counter_type<X, sizeof (meta::no)> + { + typedef X r; + }; + + template <typename X> + struct counter_type<X, sizeof (meta::yes)> + { + typedef shared_base r; + }; + + template <typename X, typename Y> + struct counter_ops; + + template <typename X> + struct counter_ops<X, X> + { + counter_ops (X const* p) : counter_ (p ? bits::counter (p) : 0) {} + counter_ops (counter_ops const& x) : counter_ (x.counter_) {} + + template <typename Y> + counter_ops (counter_ops<Y, Y> const& x) : counter_ (x.counter_) {} + + counter_ops& + operator= (counter_ops const& x) + { + counter_ = x.counter_; + return *this; + } + + template <typename Y> + counter_ops& + operator= (counter_ops<Y, Y> const& x) + { + counter_ = x.counter_; + return *this; + } + + void + reset (X const* p) + { + counter_ = p ? bits::counter (p) : 0; + } + + void + inc (X*) + { + (*counter_)++; + } + + void + dec (X* p) + { + if (--(*counter_) == 0) + { + p->~X (); + operator delete (counter_); // Counter is the top of the memory block. + } + } + + std::size_t + count (X const*) const + { + return *counter_; + } + + std::size_t* counter_; + }; + + template <typename Y> + struct counter_ops<shared_base, Y> + { + counter_ops (Y const*) {} + + void + reset (Y const*) {} + + void + inc (shared_base* p) {p->_inc_ref ();} + + void + dec (Y* p) + { + if (static_cast<shared_base*> (p)->_dec_ref ()) + delete p; + } + + std::size_t + count (shared_base const* p) const {return p->_ref_count ();} + }; + } + + template <typename X> + inline X* + inc_ref (X* p) + { + bits::counter_ops<typename bits::counter_type<X>::r, X> c (p); + c.inc (p); + return p; + } + + template <typename X> + inline void + dec_ref (X* p) + { + bits::counter_ops<typename bits::counter_type<X>::r, X> c (p); + c.dec (p); + } + + template <typename X> + inline std::size_t + ref_count (X const* p) + { + bits::counter_ops<typename bits::counter_type<X>::r, X> c (p); + return c.count (p); + } +} diff --git a/odb/traits.hxx b/odb/traits.hxx new file mode 100644 index 0000000..e8e109c --- /dev/null +++ b/odb/traits.hxx @@ -0,0 +1,93 @@ +// file : odb/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_TRAITS_HXX +#define ODB_TRAITS_HXX + +#include <memory> // std::auto_ptr + +#include <odb/forward.hxx> +#include <odb/shared-ptr.hxx> +#include <odb/shared-ptr-traits.hxx> + +namespace odb +{ + enum id_source + { + ids_assigned /* Assigned by the application. */ + }; + + // Specializations should defined the following members: + // + // id_type - object id (primary key) type + // id_source - object id (primary key) source + // id_type id (const T&) - get object id + // + // void insert (database&, const T&) + // void update (database&, const T&) + // void erase (database&, const id_type&) + // memory_traits<T>::shared_ptr find (database&, const id_type&) + // + // And inherit from object_memory and object_factory. + // + // template <typename T> + // class access::object_traits; + + template <typename T> + class access::object_memory + { + public: + typedef odb::shared_ptr<T> shared_ptr; + typedef std::auto_ptr<T> unique_ptr; + }; + + template <typename T> + class access::object_factory + { + public: + static typename object_memory<T>::shared_ptr + create () + { + // By default use shared_ptr-specific construction. + // + return shared_factory<typename object_memory<T>::shared_ptr>::create (); + } + }; + + template <typename P> + class access::shared_factory + { + public: + typedef typename shared_ptr_traits<P>::type object_type; + + static P + create () + { + void* v (shared_ptr_traits<P>::allocate (sizeof (object_type))); + guard g (v); + P p (new (v) object_type); + g.release (); + return p; + } + private: + struct guard + { + guard (void* p): p_ (p) {} + ~guard () {if (p_) shared_ptr_traits<P>::free (p_);} + void release () {p_ = 0;} + void* p_; + }; + }; + + template <typename T> + struct object_traits: access::object_traits<T> + { + typedef + shared_ptr_traits<typename access::object_traits<T>::shared_ptr> + shared_ops; + }; +} + +#endif // ODB_TRAITS_HXX diff --git a/odb/transaction.cxx b/odb/transaction.cxx new file mode 100644 index 0000000..7b28ea2 --- /dev/null +++ b/odb/transaction.cxx @@ -0,0 +1,93 @@ +// file : odb/transaction.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/transaction.hxx> + +namespace odb +{ + // + // transaction + // + + static transaction* current_transaction = 0; + + transaction:: + transaction (transaction_impl* impl) + : finilized_ (false), impl_ (impl) + { + current_transaction = this; + } + + transaction:: + ~transaction () + { + if (!finilized_) + { + try + { + rollback (); + } + catch (const database_exception&) + { + // Ignore it. + } + } + + current_transaction = 0; + delete impl_; + } + + bool transaction:: + has_current () + { + return current_transaction != 0; + } + + transaction& transaction:: + current () + { + if (current_transaction == 0) + throw not_in_transaction (); + + return *current_transaction; + } + + void transaction:: + commit () + { + if (finilized_) + throw transaction_already_finilized (); + + // Flush the session if we are in the session-per-transaction mode. + // + if (impl_->own_session ()) + session ().flush (); + + impl_->commit (); + finilized_ = true; + } + + void transaction:: + rollback () + { + if (finilized_) + throw transaction_already_finilized (); + + finilized_ = true; + impl_->rollback (); + } + + // + // transaction_impl + // + + transaction_impl:: + ~transaction_impl () + { + if (own_session ()) + reinterpret_cast<session_type*> (&session_mem_)->~session (); + } +} diff --git a/odb/transaction.hxx b/odb/transaction.hxx new file mode 100644 index 0000000..01d4a38 --- /dev/null +++ b/odb/transaction.hxx @@ -0,0 +1,139 @@ +// file : odb/transaction.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_TRANSACTION_HXX +#define ODB_TRANSACTION_HXX + +#include <new> // placement new + +#include <odb/forward.hxx> + +namespace odb +{ + class transaction_impl; + + class transaction + { + public: + typedef odb::session session_type; + typedef odb::database database_type; + + explicit + transaction (transaction_impl*); + + // Unless the transaction has been already been finilized (explicitly + // committed or rolled back), the destructor will roll it back. + // + ~transaction (); + + void + commit (); + + void + rollback (); + + // Return the database this transaction is on. + // + database_type& + database (); + + // Return the session this transaction is part of. + // + session_type& + session (); + + // Return current transaction or throw if there is no transaction + // in effect. + // + static transaction& + current (); + + // Return true if there is a transaction in effect. + // + static bool + has_current (); + + public: + transaction_impl& + implementation (); + + private: + // Copying or assignment of transactions is not supported. + // + transaction (const transaction&); + transaction& operator= (const transaction&); + + protected: + bool finilized_; + transaction_impl* impl_; + }; +} + +#include <odb/session.hxx> + +namespace odb +{ + class transaction_impl + { + protected: + friend class transaction; + + typedef odb::session session_type; + typedef odb::database database_type; + + transaction_impl (database_type& db, session_type& s) + : database_ (db), session_ (s) + { + } + + transaction_impl (database_type& db) + : database_ (db), + session_ (*reinterpret_cast<session_type*> (&session_mem_)) + { + new (&session_mem_) session_type (); + } + + virtual + ~transaction_impl (); + + virtual void + commit () = 0; + + virtual void + rollback () = 0; + + session_type& + session () + { + return session_; + } + + database_type& + database () + { + return database_; + } + + bool + own_session () const + { + return &session_ == reinterpret_cast<const session_type*> (&session_mem_); + } + + protected: + database_type& database_; + session_type& session_; + + union + { + void* align_; + char data_[sizeof (session_type)]; + } session_mem_; + }; +} + +#include <odb/transaction.ixx> + +#endif // ODB_TRANSACTION_HXX diff --git a/odb/transaction.ixx b/odb/transaction.ixx new file mode 100644 index 0000000..e6247e6 --- /dev/null +++ b/odb/transaction.ixx @@ -0,0 +1,25 @@ +// file : odb/transaction.ixx +// author : Boris Kolpackov <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +namespace odb +{ + inline transaction::database_type& transaction:: + database () + { + return impl_->database (); + } + + inline transaction::session_type& transaction:: + session () + { + return impl_->session (); + } + + inline transaction_impl& transaction:: + implementation () + { + return *impl_; + } +} |