aboutsummaryrefslogtreecommitdiff
path: root/common/session
diff options
context:
space:
mode:
Diffstat (limited to 'common/session')
-rw-r--r--common/session/custom/driver.cxx168
-rw-r--r--common/session/custom/makefile114
-rw-r--r--common/session/custom/session.cxx50
-rw-r--r--common/session/custom/session.hxx139
-rw-r--r--common/session/custom/session.txx118
-rw-r--r--common/session/custom/test.hxx119
-rw-r--r--common/session/custom/test.std0
7 files changed, 708 insertions, 0 deletions
diff --git a/common/session/custom/driver.cxx b/common/session/custom/driver.cxx
new file mode 100644
index 0000000..1ec7cbb
--- /dev/null
+++ b/common/session/custom/driver.cxx
@@ -0,0 +1,168 @@
+// file : common/session/custom/driver.cxx
+// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test custom session (C++11 only).
+//
+
+#include <memory>
+#include <cstddef> // std::size_t
+#include <cassert>
+#include <iostream>
+
+#include <odb/tracer.hxx>
+#include <odb/database.hxx>
+#include <odb/session.hxx>
+#include <odb/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "session.hxx"
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+
+using odb::database;
+using odb::transaction;
+
+struct counting_tracer: odb::tracer
+{
+ virtual void
+ execute (odb::connection&, const char*)
+ {
+ count++;
+ }
+
+ size_t count;
+};
+
+static counting_tracer tracer;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ // Simple Tech Ltd.
+ //
+ {
+ shared_ptr<employer> er (new employer ("Simple Tech Ltd", "ST"));
+
+ shared_ptr<employee> john (new employee ("John", "Doe", er));
+ shared_ptr<employee> jane (new employee ("Jane", "Doe", er));
+
+ transaction t (db->begin ());
+
+ db->persist (er);
+ db->persist (john);
+ db->persist (jane);
+
+ t.commit ();
+ }
+
+ // Complex Systems Inc.
+ //
+ {
+ shared_ptr<employer> er (new employer ("Complex Systems Inc", "CS"));
+
+ shared_ptr<employee> john (new employee ("John", "Smith", er));
+ shared_ptr<employee> jane (new employee ("Jane", "Smith", er));
+
+ transaction t (db->begin ());
+
+ db->persist (er);
+ db->persist (john);
+ db->persist (jane);
+
+ t.commit ();
+ }
+
+ session s;
+ shared_ptr<employer> st, cs;
+ shared_ptr<employee> ste, cse;
+
+ {
+ transaction t (db->begin ());
+
+ st = db->load<employer> ("Simple Tech Ltd");
+ ste = db->load<employee> (st->employees ()[0].object_id ());
+
+ // Test object cache.
+ //
+ shared_ptr<employee> e (st->employees ()[0].load ());
+ assert (ste->employer () == st);
+ assert (ste == e);
+
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+
+ cs = db->load<employer> ("Complex Systems Inc");
+ cse = db->load<employee> (cs->employees ()[0].object_id ());
+ cs->employees ()[0].load ();
+
+ t.commit ();
+ }
+
+ cs->symbol ("CSI");
+
+ // Swap employees.
+ //
+ ste->employer (cs);
+ cse->employer (st);
+ st->employees ()[0] = cse;
+ cs->employees ()[0] = ste;
+
+ {
+ transaction t (db->begin ());
+ tracer.count = 0;
+ t.tracer (tracer);
+ s.flush (*db); // Flush all the changes.
+ assert (tracer.count == 3);
+ t.commit ();
+ s.mark (); // Mark all the changed objects as unchanged.
+ }
+
+ {
+ transaction t (db->begin ());
+ tracer.count = 0;
+ t.tracer (tracer);
+ s.flush (*db);
+ assert (tracer.count == 0);
+ t.commit ();
+ }
+
+ cs->symbol ("COMP");
+ st->symbol ("SMPL");
+
+ {
+ transaction t (db->begin ());
+ tracer.count = 0;
+ t.tracer (tracer);
+ s.flush (*db); // Flush all the changes.
+ assert (tracer.count == 2);
+ t.commit ();
+ s.mark (); // Mark all the changed objects as unchanged.
+ }
+
+ {
+ transaction t (db->begin ());
+ tracer.count = 0;
+ t.tracer (tracer);
+ s.flush (*db);
+ assert (tracer.count == 0);
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/common/session/custom/makefile b/common/session/custom/makefile
new file mode 100644
index 0000000..6fb6c95
--- /dev/null
+++ b/common/session/custom/makefile
@@ -0,0 +1,114 @@
+# file : common/session/custom/makefile
+# copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC
+# license : GNU GPL v2; see accompanying LICENSE file
+
+include $(dir $(lastword $(MAKEFILE_LIST)))../../../build/bootstrap.make
+
+cxx_tun := driver.cxx session.cxx
+odb_hdr := test.hxx
+cxx_obj := $(addprefix $(out_base)/,$(cxx_tun:.cxx=.o) $(odb_hdr:.hxx=-odb.o))
+cxx_od := $(cxx_obj:.o=.o.d)
+
+common.l := $(out_root)/libcommon/common/common.l
+common.l.cpp-options := $(out_root)/libcommon/common/common.l.cpp-options
+
+driver := $(out_base)/driver
+dist := $(out_base)/.dist
+test := $(out_base)/.test
+clean := $(out_base)/.clean
+
+# Import.
+#
+$(call import,\
+ $(scf_root)/import/odb/stub.make,\
+ odb: odb,odb-rules: odb_rules)
+
+# Build.
+#
+$(driver): $(cxx_obj) $(common.l)
+$(cxx_obj) $(cxx_od): cpp_options := -I$(out_base) -I$(src_base)
+$(cxx_obj) $(cxx_od): $(common.l.cpp-options)
+
+genf := $(addprefix $(odb_hdr:.hxx=-odb),.hxx .ixx .cxx) $(odb_hdr:.hxx=.sql)
+gen := $(addprefix $(out_base)/,$(genf))
+
+$(gen): $(odb)
+$(gen): odb := $(odb)
+$(gen) $(dist): export odb_options += --database $(db_id) --generate-schema \
+--generate-session --session-type ::session --hxx-prologue \
+'\#include "session.hxx"' --table-prefix session_custom_
+$(gen): cpp_options := -I$(src_base)
+$(gen): $(common.l.cpp-options)
+
+$(call include-dep,$(cxx_od),$(cxx_obj),$(gen))
+
+# Alias for default target.
+#
+$(out_base)/: $(driver)
+
+# Dist
+#
+name := $(subst /,-,$(subst $(src_root)/common/,,$(src_base)))
+
+$(dist): db_id := @database@
+$(dist): sources := $(cxx_tun)
+$(dist): headers := $(odb_hdr)
+$(dist): data_dist := test.std
+$(dist): export name := $(name)
+$(dist): export extra_dist := $(data_dist) $(call vc10projs,$(name)) \
+$(call vc11projs,$(name))
+$(dist):
+ $(call dist-data,$(sources) $(headers) $(data_dist))
+ $(call meta-automake,../../template/Makefile.am)
+ $(call meta-vc10projs,../../template/template,$(name))
+ $(call meta-vc11projs,../../template/template,$(name))
+
+# Test.
+#
+$(test): $(driver) $(src_base)/test.std
+ $(call schema)
+ $(call message,test $<,$< --options-file $(dcf_root)/db.options \
+>$(out_base)/test.out)
+ $(call message,,diff -u $(src_base)/test.std $(out_base)/test.out)
+ $(call message,,rm -f $(out_base)/test.out)
+
+# Clean.
+#
+$(clean): \
+ $(driver).o.clean \
+ $(addsuffix .cxx.clean,$(cxx_obj)) \
+ $(addsuffix .cxx.clean,$(cxx_od)) \
+ $(addprefix $(out_base)/,$(odb_hdr:.hxx=-odb.cxx.hxx.clean))
+ $(call message,,rm -f $(out_base)/test.out)
+
+# Generated .gitignore.
+#
+ifeq ($(out_base),$(src_base))
+$(driver): | $(out_base)/.gitignore
+
+$(out_base)/.gitignore: files := driver $(genf)
+$(clean): $(out_base)/.gitignore.clean
+
+$(call include,$(bld_root)/git/gitignore.make)
+endif
+
+# How to.
+#
+$(call include,$(bld_root)/dist.make)
+$(call include,$(bld_root)/meta/vc10proj.make)
+$(call include,$(bld_root)/meta/vc11proj.make)
+$(call include,$(bld_root)/meta/automake.make)
+
+$(call include,$(bld_root)/cxx/standard.make) # cxx_standard
+ifdef cxx_standard
+$(gen): odb_options += --std $(cxx_standard)
+$(call include,$(odb_rules))
+endif
+
+$(call include,$(bld_root)/cxx/cxx-d.make)
+$(call include,$(bld_root)/cxx/cxx-o.make)
+$(call include,$(bld_root)/cxx/o-e.make)
+
+# Dependencies.
+#
+$(call import,$(src_root)/libcommon/makefile)
diff --git a/common/session/custom/session.cxx b/common/session/custom/session.cxx
new file mode 100644
index 0000000..b50493f
--- /dev/null
+++ b/common/session/custom/session.cxx
@@ -0,0 +1,50 @@
+// file : common/session/custom/session.cxx
+// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include "session.hxx"
+
+static session* current_; // Use TLS in multi-threaded applications.
+
+session::
+session ()
+{
+ assert (current_ == 0);
+ current_ = this;
+}
+
+session::
+~session ()
+{
+ assert (current_ == this);
+ current_ = 0;
+}
+
+bool session::
+has_current ()
+{
+ return current_ != 0;
+}
+
+session& session::
+current ()
+{
+ assert (current_ != 0);
+ return *current_;
+}
+
+void session::
+flush (odb::database& db)
+{
+ for (type_map::iterator i (map_.begin ()), e (map_.end ()); i != e; ++i)
+ i->second->flush (db);
+}
+
+void session::
+mark ()
+{
+ for (type_map::iterator i (map_.begin ()), e (map_.end ()); i != e; ++i)
+ i->second->mark ();
+}
diff --git a/common/session/custom/session.hxx b/common/session/custom/session.hxx
new file mode 100644
index 0000000..bb60a4b
--- /dev/null
+++ b/common/session/custom/session.hxx
@@ -0,0 +1,139 @@
+// file : common/session/custom/session.hxx
+// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef SESSION_HXX
+#define SESSION_HXX
+
+#include <map>
+#include <memory>
+#include <typeinfo>
+
+#include <odb/database.hxx>
+#include <odb/traits.hxx> // odb::object_traits
+#include <odb/details/type-info.hxx> // odb::details::type_info_comparator
+
+// This custom session implementation assumes we are working with
+// one database at a time.
+//
+class session
+{
+public:
+ session ();
+ ~session ();
+
+private:
+ session (const session&);
+ session& operator= (const session&);
+
+ // Current session interface.
+ //
+public:
+ static bool
+ has_current ();
+
+ static session&
+ current ();
+
+ // Change tracking interface.
+ //
+ // Call flush() within a transaction to apply the changes to the
+ // database. Then after successfully committing the transaction,
+ // call mark() to mark all the changed objects as again unchanged.
+ //
+public:
+ void
+ flush (odb::database&);
+
+ void
+ mark ();
+
+private:
+ struct object_map_base
+ {
+ virtual
+ ~object_map_base () {}
+
+ virtual void
+ flush (odb::database&) = 0;
+
+ virtual void
+ mark () = 0;
+ };
+
+ template <typename T>
+ struct object_state
+ {
+ typedef typename odb::object_traits<T>::pointer_type pointer_type;
+
+ explicit
+ object_state (pointer_type o): obj (o), flushed_ (false) {}
+
+ pointer_type obj;
+ pointer_type orig;
+ bool flushed_;
+ };
+
+ template <typename T>
+ struct object_map: object_map_base,
+ std::map<typename odb::object_traits<T>::id_type,
+ object_state<T> >
+ {
+ virtual void
+ flush (odb::database&);
+
+ virtual void
+ mark ();
+ };
+
+ // Object cache interface.
+ //
+public:
+ template <typename T>
+ struct position
+ {
+ typedef object_map<T> map;
+ typedef typename map::iterator iterator;
+
+ position () {}
+ position (map& m, const iterator& p): map_ (&m), pos_ (p) {}
+
+ map* map_;
+ iterator pos_;
+ };
+
+ template <typename T>
+ position<T>
+ insert (odb::database&,
+ const typename odb::object_traits<T>::id_type&,
+ const typename odb::object_traits<T>::pointer_type&);
+
+ template <typename T>
+ static void
+ initialize (const position<T>&);
+
+ template <typename T>
+ typename odb::object_traits<T>::pointer_type
+ find (odb::database&, const typename odb::object_traits<T>::id_type&) const;
+
+ template <typename T>
+ void
+ erase (odb::database&, const typename odb::object_traits<T>::id_type&);
+
+ template <typename T>
+ static void
+ erase (const position<T>& p)
+ {
+ p.map_->erase (p.pos_);
+ }
+
+private:
+ typedef std::map<const std::type_info*,
+ std::shared_ptr<object_map_base>,
+ odb::details::type_info_comparator> type_map;
+ type_map map_;
+};
+
+#include "session.txx"
+
+#endif // SESSION_HXX
diff --git a/common/session/custom/session.txx b/common/session/custom/session.txx
new file mode 100644
index 0000000..203b4e3
--- /dev/null
+++ b/common/session/custom/session.txx
@@ -0,0 +1,118 @@
+// file : common/session/custom/session.txx
+// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+template <typename T>
+typename session::position<T> session::
+insert (odb::database&,
+ const typename odb::object_traits<T>::id_type& id,
+ const typename odb::object_traits<T>::pointer_type& obj)
+{
+ typedef odb::object_traits<T> object_traits;
+
+ std::shared_ptr<object_map_base>& pm (map_[&typeid (T)]);
+
+ if (!pm)
+ pm.reset (new object_map<T>);
+
+ object_map<T>& m (static_cast<object_map<T>&> (*pm));
+
+ typename object_map<T>::value_type vt (id, object_state<T> (obj));
+ std::pair<typename object_map<T>::iterator, bool> r (m.insert (vt));
+
+ // We shall never try to re-insert the same object into the cache.
+ //
+ assert (r.second);
+
+ return position<T> (m, r.first);
+}
+
+template <typename T>
+void session::
+initialize (const position<T>& p)
+{
+ typedef typename odb::object_traits<T>::pointer_type pointer_type;
+
+ // Make a copy for change tracking. If our object model had a
+ // polymorphic hierarchy, then we would have had to use a
+ // virtual function-based mechanism (e.g., clone()) instead of
+ // the copy constructor since for a polymorphic hierarchy all
+ // the derived objects are stored as pointers to the root object.
+ //
+ p.pos_->second.orig = pointer_type (new T (*p.pos_->second.obj));
+}
+
+template <typename T>
+typename odb::object_traits<T>::pointer_type session::
+find (odb::database&, const typename odb::object_traits<T>::id_type& id) const
+{
+ typedef typename odb::object_traits<T>::pointer_type pointer_type;
+
+ type_map::const_iterator ti (map_.find (&typeid (T)));
+
+ if (ti == map_.end ())
+ return pointer_type ();
+
+ const object_map<T>& m (static_cast<const object_map<T>&> (*ti->second));
+ typename object_map<T>::const_iterator oi (m.find (id));
+
+ if (oi == m.end ())
+ return pointer_type ();
+
+ return oi->second.obj;
+}
+
+template <typename T>
+void session::
+erase (odb::database&, const typename odb::object_traits<T>::id_type& id)
+{
+ type_map::iterator ti (map_.find (&typeid (T)));
+
+ if (ti == map_.end ())
+ return;
+
+ object_map<T>& m (static_cast<object_map<T>&> (*ti->second));
+ typename object_map<T>::iterator oi (m.find (id));
+
+ if (oi == m.end ())
+ return;
+
+ m.erase (oi);
+
+ if (m.empty ())
+ map_.erase (ti);
+}
+
+template <typename T>
+void session::object_map<T>::
+flush (odb::database& db)
+{
+ for (typename object_map<T>::iterator i (this->begin ()), e (this->end ());
+ i != e; ++i)
+ {
+ const T& obj (*i->second.obj);
+
+ if (obj.changed (*i->second.orig))
+ {
+ db.update (obj);
+ i->second.flushed_ = true;
+ }
+ }
+}
+
+template <typename T>
+void session::object_map<T>::
+mark ()
+{
+ for (typename object_map<T>::iterator i (this->begin ()), e (this->end ());
+ i != e; ++i)
+ {
+ if (i->second.flushed_)
+ {
+ i->second.orig.reset (new T (*i->second.obj));
+ i->second.flushed_ = false;
+ }
+ }
+}
diff --git a/common/session/custom/test.hxx b/common/session/custom/test.hxx
new file mode 100644
index 0000000..1c4a713
--- /dev/null
+++ b/common/session/custom/test.hxx
@@ -0,0 +1,119 @@
+// file : common/session/custom/test.hxx
+// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <memory>
+#include <vector>
+
+#include <odb/core.hxx>
+#include <odb/lazy-ptr.hxx>
+
+class employee;
+
+#pragma db object pointer(std::shared_ptr) session
+class employer
+{
+public:
+ employer (const std::string& name, const std::string& symbol)
+ : name_ (name), symbol_ (symbol) {}
+
+ const std::string&
+ name () const {return name_;}
+
+ const std::string&
+ symbol () const {return symbol_;}
+
+ void
+ symbol (const std::string& symbol) {symbol_ = symbol;}
+
+ // Employees of this employer.
+ //
+ typedef std::vector<odb::lazy_weak_ptr<employee>> employees_type;
+
+ const employees_type&
+ employees () const {return employees_;}
+
+ employees_type&
+ employees () {return employees_;}
+
+ // Change tracking.
+ //
+public:
+ bool
+ changed (const employer& orig) const
+ {
+ // Note that we don't need to track object ids, inverse pointers, nor
+ // readonly/const data members.
+ //
+ return symbol_ != orig.symbol_;
+ }
+
+private:
+ friend class odb::access;
+ employer () {}
+
+ #pragma db id
+ std::string name_;
+
+ std::string symbol_;
+
+ #pragma db value_not_null inverse(employer_)
+ employees_type employees_;
+};
+
+#pragma db object pointer(std::shared_ptr) session
+class employee
+{
+public:
+ typedef ::employer employer_type;
+
+ employee (const std::string& first,
+ const std::string& last,
+ std::shared_ptr<employer_type> employer)
+ : first_ (first), last_ (last), employer_ (employer) {}
+
+ // Name.
+ //
+ const std::string&
+ first () const {return first_;}
+
+ const std::string&
+ last () const {return last_;}
+
+ // Employer.
+ //
+ std::shared_ptr<employer_type>
+ employer () const {return employer_;}
+
+ void
+ employer (std::shared_ptr<employer_type> e) {employer_ = e;}
+
+ // Change tracking.
+ //
+public:
+ bool
+ changed (const employee& orig) const
+ {
+ return first_ != orig.first_ || last_ != orig.last_ ||
+ employer_ != orig.employer_;
+ }
+
+private:
+ friend class odb::access;
+ employee () {}
+
+ #pragma db id auto
+ unsigned long id_;
+
+ std::string first_;
+ std::string last_;
+
+ #pragma db not_null
+ std::shared_ptr<employer_type> employer_;
+};
+
+#endif // TEST_HXX
diff --git a/common/session/custom/test.std b/common/session/custom/test.std
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/common/session/custom/test.std