From 4f22837bda784e29f17750c8f1d623b40c1093d4 Mon Sep 17 00:00:00 2001
From: Boris Kolpackov <boris@codesynthesis.com>
Date: Wed, 4 Dec 2013 11:30:33 +0200
Subject: Implement on_delete pragma for object pointers

Translates to the ON DELETE SQL clause.
---
 common/makefile                          |   5 +-
 common/relationship-query/driver.cxx     | 173 ------------------
 common/relationship-query/makefile       | 115 ------------
 common/relationship-query/test.hxx       | 151 ----------------
 common/relationship-query/test.std       |   0
 common/relationship/basics/driver.cxx    | 159 +++++++++++++++++
 common/relationship/basics/makefile      | 115 ++++++++++++
 common/relationship/basics/test.hxx      | 291 +++++++++++++++++++++++++++++++
 common/relationship/basics/test.std      |   0
 common/relationship/driver.cxx           | 159 -----------------
 common/relationship/makefile             | 115 ------------
 common/relationship/on-delete/driver.cxx |  81 +++++++++
 common/relationship/on-delete/makefile   | 117 +++++++++++++
 common/relationship/on-delete/test.hxx   |  59 +++++++
 common/relationship/on-delete/test.std   |   0
 common/relationship/query/driver.cxx     | 173 ++++++++++++++++++
 common/relationship/query/makefile       | 115 ++++++++++++
 common/relationship/query/test.hxx       | 151 ++++++++++++++++
 common/relationship/query/test.std       |   0
 common/relationship/test.hxx             | 291 -------------------------------
 common/relationship/test.std             |   0
 21 files changed, 1264 insertions(+), 1006 deletions(-)
 delete mode 100644 common/relationship-query/driver.cxx
 delete mode 100644 common/relationship-query/makefile
 delete mode 100644 common/relationship-query/test.hxx
 delete mode 100644 common/relationship-query/test.std
 create mode 100644 common/relationship/basics/driver.cxx
 create mode 100644 common/relationship/basics/makefile
 create mode 100644 common/relationship/basics/test.hxx
 create mode 100644 common/relationship/basics/test.std
 delete mode 100644 common/relationship/driver.cxx
 delete mode 100644 common/relationship/makefile
 create mode 100644 common/relationship/on-delete/driver.cxx
 create mode 100644 common/relationship/on-delete/makefile
 create mode 100644 common/relationship/on-delete/test.hxx
 create mode 100644 common/relationship/on-delete/test.std
 create mode 100644 common/relationship/query/driver.cxx
 create mode 100644 common/relationship/query/makefile
 create mode 100644 common/relationship/query/test.hxx
 create mode 100644 common/relationship/query/test.std
 delete mode 100644 common/relationship/test.hxx
 delete mode 100644 common/relationship/test.std

diff --git a/common/makefile b/common/makefile
index 1ad2389..8aa48c1 100644
--- a/common/makefile
+++ b/common/makefile
@@ -38,8 +38,9 @@ prepared                  \
 query/basics              \
 query/array               \
 readonly                  \
-relationship              \
-relationship-query        \
+relationship/basics       \
+relationship/on-delete    \
+relationship/query        \
 schema/namespace          \
 schema/embedded/basics    \
 schema/embedded/order     \
diff --git a/common/relationship-query/driver.cxx b/common/relationship-query/driver.cxx
deleted file mode 100644
index 785aa94..0000000
--- a/common/relationship-query/driver.cxx
+++ /dev/null
@@ -1,173 +0,0 @@
-// file      : common/relationship-query/driver.cxx
-// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
-// license   : GNU GPL v2; see accompanying LICENSE file
-
-// Test relationship queries.
-//
-
-#include <memory>   // std::auto_ptr
-#include <cassert>
-#include <iostream>
-
-#include <odb/database.hxx>
-#include <odb/session.hxx>
-#include <odb/transaction.hxx>
-
-#include <common/config.hxx> // HAVE_CXX11, HAVE_TR1_MEMORY
-#include <common/common.hxx>
-
-#include "test.hxx"
-#include "test-odb.hxx"
-
-using namespace std;
-using namespace odb::core;
-
-int
-main (int argc, char* argv[])
-{
-  try
-  {
-    auto_ptr<database> db (create_database (argc, argv));
-
-#if defined(HAVE_CXX11) || defined(HAVE_TR1_MEMORY)
-
-    //
-    //
-    {
-      shared_ptr<country> ca (new country ("CA", "Canada"));
-      shared_ptr<country> za (new country ("ZA", "South Africa"));
-      shared_ptr<country> us (new country ("US", "United States"));
-      shared_ptr<country> se (new country ("SE", "Sweden"));
-
-      shared_ptr<employer> st (new employer ("Simple Tech, Inc", ca));
-      shared_ptr<employer> ct (new employer ("Complex Tech, Inc", us));
-
-      // person
-      //
-      shared_ptr<person> p1 (
-        new person (1, "John", "Doe", 30, ca, true, za));
-
-      shared_ptr<person> p2 (
-        new person (2, "Jane", "Doe", 29, za, false, us));
-      p2->husband = p1;
-
-      shared_ptr<person> p3 (
-        new person (3, "Joe", "Dirt", 31,  us, true, us));
-
-      shared_ptr<person> p4 (
-        new person (4, "Johan", "Johansen", 32, se, false, ca));
-
-      // employee
-      //
-      shared_ptr<employee> e1 (
-        new employee (1, "John", "Doe", 30, ca, true, za, st));
-
-      shared_ptr<employee> e2 (
-        new employee (2, "Jane", "Doe", 29, za, false, us, ct));
-      e2->husband = p1;
-
-      shared_ptr<employee> e3 (
-        new employee (3, "Joe", "Dirt", 31,  us, true, us, st));
-
-      shared_ptr<employee> e4 (
-        new employee (4, "Johan", "Johansen", 32, se, false, ca, ct));
-
-      transaction t (db->begin ());
-      db->persist (ca);
-      db->persist (za);
-      db->persist (us);
-      db->persist (se);
-
-      db->persist (st);
-      db->persist (ct);
-
-      db->persist (p1);
-      db->persist (p2);
-      db->persist (p3);
-      db->persist (p4);
-
-      db->persist (e1);
-      db->persist (e2);
-      db->persist (e3);
-      db->persist (e4);
-      t.commit ();
-    }
-
-    typedef odb::query<person> p_query;
-    typedef odb::result<person> p_result;
-
-    typedef odb::query<employee> e_query;
-    typedef odb::result<employee> e_result;
-
-    // Make sure we have an independent JOIN for each relationship.
-    //
-    {
-      session s;
-      transaction t (db->begin ());
-
-      p_result pr (db->query<person> (
-                   p_query::residence.location->code == "ZA"));
-      assert (size (pr) == 1);
-
-      e_result er (db->query<employee> (
-                   e_query::residence.location->code == "ZA"));
-      assert (size (er) == 1);
-
-      t.commit ();
-    }
-
-    // Test Self-JOIN.
-    //
-    {
-      session s;
-      transaction t (db->begin ());
-
-      p_result pr (db->query<person> (p_query::husband->last_name == "Doe"));
-      assert (size (pr) == 1);
-
-      e_result er (db->query<employee> (e_query::husband->last_name == "Doe"));
-      assert (size (er) == 1);
-
-      t.commit ();
-    }
-
-    // Test query conditions from both base and derived.
-    //
-    {
-      session s;
-      transaction t (db->begin ());
-
-      e_result r (
-        db->query<employee> (
-          e_query::employed_by->name == "Simple Tech, Inc" &&
-          e_query::nationality->code == "US"));
-
-      assert (size (r) == 1);
-
-      t.commit ();
-    }
-
-    // Test second-level pointers.
-    //
-    {
-      session s;
-      transaction t (db->begin ());
-
-      p_result r (
-        db->query<person> (
-          p_query::husband->residence.location == "CA"));
-
-      assert (size (r) == 1);
-
-      t.commit ();
-    }
-
-#endif // HAVE_CXX11 || HAVE_TR1_MEMORY
-
-  }
-  catch (const odb::exception& e)
-  {
-    cerr << e.what () << endl;
-    return 1;
-  }
-}
diff --git a/common/relationship-query/makefile b/common/relationship-query/makefile
deleted file mode 100644
index 14d5e5c..0000000
--- a/common/relationship-query/makefile
+++ /dev/null
@@ -1,115 +0,0 @@
-# file      : common/relationship-query/makefile
-# copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
-# license   : GNU GPL v2; see accompanying LICENSE file
-
-include $(dir $(lastword $(MAKEFILE_LIST)))../../build/bootstrap.make
-
-cxx_tun := driver.cxx
-odb_hdr := test.hxx
-genf    := $(call odb-gen,$(odb_hdr))
-gen     := $(addprefix $(out_base)/,$(genf))
-cxx_obj := $(addprefix $(out_base)/,$(cxx_tun:.cxx=.o)) $(filter %.o,$(gen:.cxx=.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
-
-# 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)
-
-$(gen): $(odb)
-$(gen): odb := $(odb)
-$(gen) $(dist): export odb_options += --generate-schema --generate-query \
---generate-session --table-prefix relationship_query_
-$(gen): cpp_options := -I$(src_base)
-$(gen): $(common.l.cpp-options)
-
-ifneq ($(db_id),common)
-$(gen): odb_options += --database $(db_id)
-else
-$(gen): odb_options += --multi-database dynamic
-endif
-
-$(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): sources := $(cxx_tun)
-$(dist): headers := $(odb_hdr)
-$(dist): data_dist := test.std
-$(dist): export name := $(name)
-$(dist): export extra_dist := $(data_dist) $(call vc8projs,$(name)) \
-$(call vc9projs,$(name)) $(call vc10projs,$(name)) $(call vc11projs,$(name))
-$(dist):
-	$(call dist-data,$(sources) $(headers) $(data_dist))
-	$(call meta-automake,../template/Makefile.am)
-	$(call meta-vc8projs,../template/template,$(name))
-	$(call meta-vc9projs,../template/template,$(name))
-	$(call meta-vc10projs,../template/template,$(name))
-	$(call meta-vc11projs,../template/template,$(name))
-
-# Test.
-#
-ifneq ($(db_id),common)
-$(eval $(call test-rule))
-else
-$(foreach d,$(databases),$(eval $(call test-rule,$d)))
-endif
-
-# Clean.
-#
-$(clean):                            \
-  $(driver).o.clean                  \
-  $(addsuffix .cxx.clean,$(cxx_obj)) \
-  $(addsuffix .cxx.clean,$(cxx_od))  \
-  $(addsuffix .hxx.clean,$(filter %.cxx,$(gen)))
-	$(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/vc8proj.make)
-$(call include,$(bld_root)/meta/vc9proj.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/relationship-query/test.hxx b/common/relationship-query/test.hxx
deleted file mode 100644
index b2f9568..0000000
--- a/common/relationship-query/test.hxx
+++ /dev/null
@@ -1,151 +0,0 @@
-// file      : common/relationship-query/test.hxx
-// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
-// license   : GNU GPL v2; see accompanying LICENSE file
-
-#ifndef TEST_HXX
-#define TEST_HXX
-
-#include <common/config.hxx> // HAVE_CXX11, HAVE_TR1_MEMORY
-
-#if defined(HAVE_CXX11) || defined(HAVE_TR1_MEMORY)
-
-#include <string>
-
-#include <odb/core.hxx>
-
-#ifdef HAVE_CXX11
-#  include <memory>
-using std::shared_ptr;
-#else
-#  include <odb/tr1/memory.hxx>
-using std::tr1::shared_ptr;
-#endif
-
-struct country;
-
-#pragma db value
-struct residence_info
-{
-  residence_info (bool p, shared_ptr<country> l)
-      : permanent (p), location (l)
-  {
-  }
-
-  residence_info ()
-  {
-  }
-
-  bool permanent;
-
-  #pragma db not_null
-  shared_ptr<country> location;
-};
-
-#pragma db object pointer(shared_ptr)
-struct person
-{
-  person (unsigned long i,
-          const std::string& fn,
-          const std::string& ln,
-          unsigned short a,
-          shared_ptr<country> r,
-          bool p,
-          shared_ptr<country> n)
-      : id (i),
-        first_name (fn),
-        last_name (ln),
-        age (a),
-        residence (p, r),
-        nationality (n)
-  {
-  }
-
-  person ()
-  {
-  }
-
-  #pragma db id
-  unsigned long id;
-
-  #pragma db column ("first")
-  std::string first_name;
-
-  #pragma db column ("last")
-  std::string last_name;
-
-  unsigned short age;
-
-  residence_info residence;
-
-  #pragma db not_null
-  shared_ptr<country> nationality;
-
-  shared_ptr<person> husband; // Self-join.
-};
-
-struct employer;
-
-#pragma db object pointer(shared_ptr)
-struct employee: person
-{
-  employee (unsigned long i,
-          const std::string& fn,
-          const std::string& ln,
-          unsigned short a,
-          shared_ptr<country> r,
-          bool p,
-          shared_ptr<country> n,
-          shared_ptr<employer> e)
-      : person (i, fn, ln, a, r, p, n),
-        employed_by (e)
-  {
-  }
-
-  employee ()
-  {
-  }
-
-  shared_ptr<employer> employed_by;
-};
-
-#pragma db object pointer(shared_ptr)
-struct employer
-{
-  employer (const std::string& n, shared_ptr<country> nat)
-      : name (n), nationality (nat)
-  {
-  }
-
-  employer ()
-  {
-  }
-
-  #pragma db id
-  std::string name;
-
-  // The same member name and type as in person (test JOIN alias).
-  //
-  #pragma db not_null
-  shared_ptr<country> nationality;
-};
-
-#pragma db object pointer(shared_ptr)
-struct country
-{
-  country (const std::string& c, std::string const& n)
-      : code (c), name (n)
-  {
-  }
-
-  country ()
-  {
-  }
-
-  #pragma db id
-  std::string code; // ISO 2-letter country code.
-
-  std::string name;
-};
-
-#endif // HAVE_CXX11 || HAVE_TR1_MEMORY
-#endif // TEST_HXX
diff --git a/common/relationship-query/test.std b/common/relationship-query/test.std
deleted file mode 100644
index e69de29..0000000
diff --git a/common/relationship/basics/driver.cxx b/common/relationship/basics/driver.cxx
new file mode 100644
index 0000000..8ad866f
--- /dev/null
+++ b/common/relationship/basics/driver.cxx
@@ -0,0 +1,159 @@
+// file      : common/relationship/basics/driver.cxx
+// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
+// license   : GNU GPL v2; see accompanying LICENSE file
+
+// Test object relationships.
+//
+
+#include <memory>   // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+  try
+  {
+    auto_ptr<database> db (create_database (argc, argv));
+
+    aggr a ("aggr");
+    a.o1 = new obj1 ("o1", "obj1");
+    a.o2.reset (new obj2 ("obj2"));
+
+#ifdef HAVE_CXX11
+    a.v2.push_back (obj2_ptr (new obj2 ("v1 obj2 1")));
+    a.v2.push_back (0);
+    a.v2.push_back (obj2_ptr (new obj2 ("v1 obj2 2")));
+#endif
+
+#if defined(HAVE_CXX11) || defined(HAVE_TR1_MEMORY)
+    a.o3.reset (new obj3 ("obj3"));
+
+    a.c.num = 123;
+    a.c.o3.reset (new obj3 ("c"));
+
+    a.cv.push_back (comp (234, obj3_ptr (new obj3 ("cv 0"))));
+    a.cv.push_back (comp (235, obj3_ptr ()));
+    a.cv.push_back (comp (236, obj3_ptr (new obj3 ("cv 2"))));
+#endif
+
+    a.v1.push_back (new obj1 ("v1 0", "v1 0"));
+    a.v1.push_back (0);
+    a.v1.push_back (new obj1 ("v1 2", "v1 2"));
+
+    // Set cannot contain NULL pointers.
+    //
+    a.s1.insert (new obj1 ("s1 0", "s1 0"));
+    a.s1.insert (new obj1 ("s1 2", "s1 2"));
+
+    a.m1[0] = new obj1 ("m1 0", "m1 0");
+    a.m1[1] = 0;
+    a.m1[2] = new obj1 ("m1 2", "m1 2");
+
+    // persist
+    //
+    {
+      transaction t (db->begin ());
+      db->persist (a.o1);
+      db->persist (a.o2);
+
+#ifdef HAVE_CXX11
+      for (obj2_vec::iterator i (a.v2.begin ()); i != a.v2.end (); ++i)
+        if (*i)
+          db->persist (*i);
+#endif
+
+#if defined(HAVE_CXX11) || defined(HAVE_TR1_MEMORY)
+      db->persist (a.o3);
+
+      db->persist (a.c.o3);
+
+      for (comp_vec::iterator i (a.cv.begin ()); i != a.cv.end (); ++i)
+        if (i->o3)
+          db->persist (i->o3);
+#endif
+
+      for (obj1_vec::iterator i (a.v1.begin ()); i != a.v1.end (); ++i)
+        if (*i)
+          db->persist (*i);
+
+      for (obj1_set::iterator i (a.s1.begin ()); i != a.s1.end (); ++i)
+        if (*i)
+          db->persist (*i);
+
+      for (obj1_map::iterator i (a.m1.begin ()); i != a.m1.end (); ++i)
+        if (i->second)
+          db->persist (i->second);
+
+      db->persist (a);
+      t.commit ();
+    }
+
+    // load & compare
+    //
+    {
+      transaction t (db->begin ());
+      auto_ptr<aggr> a1 (db->load<aggr> (a.id));
+      t.commit ();
+
+      assert (*a1 == a);
+    }
+
+    // query
+    //
+    typedef odb::query<aggr> query;
+    typedef odb::result<aggr> result;
+
+    {
+      transaction t (db->begin ());
+
+      result r (db->query<aggr> (query::o1->str == "obj1"));
+      assert (!r.empty ());
+      assert (r.begin ()->o1->id == a.o1->id);
+      assert (size (r) == 1);
+
+      t.commit ();
+    }
+
+    // Test NULL pointer.
+    //
+    delete a.o1;
+    a.o1 = 0;
+    a.o2.reset ();
+#if defined(HAVE_CXX11) || defined(HAVE_TR1_MEMORY)
+    a.o3.reset ();
+#endif
+
+    {
+      transaction t (db->begin ());
+      db->update (a);
+      t.commit ();
+    }
+
+    // load & compare
+    //
+    {
+      transaction t (db->begin ());
+      auto_ptr<aggr> a1 (db->load<aggr> (a.id));
+      t.commit ();
+
+      assert (*a1 == a);
+    }
+  }
+  catch (const odb::exception& e)
+  {
+    cerr << e.what () << endl;
+    return 1;
+  }
+}
diff --git a/common/relationship/basics/makefile b/common/relationship/basics/makefile
new file mode 100644
index 0000000..068f60a
--- /dev/null
+++ b/common/relationship/basics/makefile
@@ -0,0 +1,115 @@
+# file      : common/relationship/basics/makefile
+# copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
+# license   : GNU GPL v2; see accompanying LICENSE file
+
+include $(dir $(lastword $(MAKEFILE_LIST)))../../../build/bootstrap.make
+
+cxx_tun := driver.cxx
+odb_hdr := test.hxx
+genf    := $(call odb-gen,$(odb_hdr))
+gen     := $(addprefix $(out_base)/,$(genf))
+cxx_obj := $(addprefix $(out_base)/,$(cxx_tun:.cxx=.o)) $(filter %.o,$(gen:.cxx=.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
+
+# 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)
+
+$(gen): $(odb)
+$(gen): odb := $(odb)
+$(gen) $(dist): export odb_options += --generate-schema --generate-query \
+--table-prefix t_rel_basics_
+$(gen): cpp_options := -I$(src_base)
+$(gen): $(common.l.cpp-options)
+
+ifneq ($(db_id),common)
+$(gen): odb_options += --database $(db_id)
+else
+$(gen): odb_options += --multi-database dynamic
+endif
+
+$(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): sources := $(cxx_tun)
+$(dist): headers := $(odb_hdr)
+$(dist): data_dist := test.std
+$(dist): export name := $(name)
+$(dist): export extra_dist := $(data_dist) $(call vc8projs,$(name)) \
+$(call vc9projs,$(name)) $(call vc10projs,$(name)) $(call vc11projs,$(name))
+$(dist):
+	$(call dist-data,$(sources) $(headers) $(data_dist))
+	$(call meta-automake,../template/Makefile.am)
+	$(call meta-vc8projs,../template/template,$(name))
+	$(call meta-vc9projs,../template/template,$(name))
+	$(call meta-vc10projs,../template/template,$(name))
+	$(call meta-vc11projs,../template/template,$(name))
+
+# Test.
+#
+ifneq ($(db_id),common)
+$(eval $(call test-rule))
+else
+$(foreach d,$(databases),$(eval $(call test-rule,$d)))
+endif
+
+# Clean.
+#
+$(clean):                            \
+  $(driver).o.clean                  \
+  $(addsuffix .cxx.clean,$(cxx_obj)) \
+  $(addsuffix .cxx.clean,$(cxx_od))  \
+  $(addsuffix .hxx.clean,$(filter %.cxx,$(gen)))
+	$(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/vc8proj.make)
+$(call include,$(bld_root)/meta/vc9proj.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/relationship/basics/test.hxx b/common/relationship/basics/test.hxx
new file mode 100644
index 0000000..a320c00
--- /dev/null
+++ b/common/relationship/basics/test.hxx
@@ -0,0 +1,291 @@
+// file      : common/relationship/basics/test.hxx
+// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
+// license   : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <common/config.hxx> // HAVE_CXX11, HAVE_TR1_MEMORY
+
+#include <set>
+#include <map>
+#include <vector>
+#include <string>
+#include <memory>
+
+#include <odb/core.hxx>
+
+#if !defined(HAVE_CXX11) && defined(HAVE_TR1_MEMORY)
+#  include <odb/tr1/memory.hxx>
+#endif
+
+// Raw pointer.
+//
+#pragma db object pointer(obj1*)
+struct obj1
+{
+  obj1 () {}
+  obj1 (const std::string& i, const std::string& s): id (i), str (s) {}
+
+  #pragma db id
+  std::string id;
+  std::string str;
+};
+
+inline bool
+operator== (const obj1& x, const obj1& y)
+{
+  return x.id == y.id && x.str == y.str;
+}
+
+// vector
+//
+typedef std::vector<obj1*> obj1_vec;
+
+inline bool
+operator== (const obj1_vec& x, const obj1_vec& y)
+{
+  if (x.size () != y.size ())
+    return false;
+
+  for (obj1_vec::size_type i (0); i < x.size (); ++i)
+    if (!(x[i] ? (y[i] && *x[i] == *y[i]) : !y[i]))
+      return false;
+
+  return true;
+}
+
+// set
+//
+struct obj1_cmp
+{
+  bool
+  operator() (obj1* x, obj1* y) const
+  {
+    return (!x || !y) ? x < y : x->id < y->id;
+  }
+};
+
+typedef std::set<obj1*, obj1_cmp> obj1_set;
+
+inline bool
+operator== (const obj1_set& x, const obj1_set& y)
+{
+  if (x.size () != y.size ())
+    return false;
+
+  for (obj1_set::const_iterator i (x.begin ()); i != x.end (); ++i)
+  {
+    obj1_set::const_iterator j (y.find (*i));
+
+    if (j == y.end ())
+      return false;
+
+    obj1* x (*i);
+    obj1* y (*j);
+
+    if (!(x ? (y && *x == *y) : !y))
+      return false;
+  }
+
+  return true;
+}
+
+// map
+//
+typedef std::map<int, obj1*> obj1_map;
+
+inline bool
+operator== (const obj1_map& x, const obj1_map& y)
+{
+  if (x.size () != y.size ())
+    return false;
+
+  for (obj1_map::const_iterator i (x.begin ()); i != x.end (); ++i)
+  {
+    obj1_map::const_iterator j (y.find (i->first));
+
+    if (j == y.end ())
+      return false;
+
+    obj1* x (i->second);
+    obj1* y (j->second);
+
+    if (!(x ? (y && *x == *y) : !y))
+      return false;
+  }
+
+  return true;
+}
+
+// auto_ptr/unique_ptr
+//
+struct obj2;
+
+#ifdef HAVE_CXX11
+typedef std::unique_ptr<obj2> obj2_ptr;
+#else
+typedef std::auto_ptr<obj2> obj2_ptr;
+#endif
+
+#pragma db object pointer(obj2_ptr)
+struct obj2
+{
+  obj2 () {}
+  obj2 (const std::string& s): str (s) {}
+
+  #pragma db id auto
+  unsigned long id;
+
+  std::string str;
+};
+
+inline bool
+operator== (const obj2& x, const obj2& y)
+{
+  return x.id == y.id && x.str == y.str;
+}
+
+#ifdef HAVE_CXX11
+typedef std::vector<obj2_ptr> obj2_vec;
+
+inline bool
+operator== (const obj2_vec& x, const obj2_vec& y)
+{
+  if (x.size () != y.size ())
+    return false;
+
+  for (obj2_vec::size_type i (0); i < x.size (); ++i)
+    if (!(x[i] ? (y[i] && *x[i] == *y[i]) : !y[i]))
+      return false;
+
+  return true;
+}
+#endif
+
+// shared_ptr
+//
+#if defined(HAVE_CXX11) || defined(HAVE_TR1_MEMORY)
+struct obj3;
+
+#ifdef HAVE_CXX11
+typedef std::shared_ptr<obj3> obj3_ptr;
+#else
+typedef std::tr1::shared_ptr<obj3> obj3_ptr;
+#endif
+
+#pragma db object pointer(obj3_ptr)
+struct obj3
+{
+  obj3 () {}
+  obj3 (const std::string& s): str (s) {}
+
+  #pragma db id auto
+  unsigned long id;
+
+  std::string str;
+};
+
+inline bool
+operator== (const obj3& x, const obj3& y)
+{
+  return x.id == y.id && x.str == y.str;
+}
+
+// composite
+//
+#pragma db value
+struct comp
+{
+  comp () {}
+  comp (int n, obj3_ptr o): num (n), o3 (o) {}
+
+  int num;
+  obj3_ptr o3;
+};
+
+inline bool
+operator== (const comp& x, const comp& y)
+{
+  return x.num == y.num &&
+    (x.o3 ? (y.o3 && *x.o3 == *y.o3) : !y.o3);
+}
+
+typedef std::vector<comp> comp_vec;
+#endif
+
+//
+//
+#pragma db object
+struct aggr
+{
+  aggr (): o1 (0) {}
+  aggr (const std::string& s): o1 (0), str (s) {}
+
+  ~aggr ()
+  {
+    delete o1;
+
+    for (obj1_vec::iterator i (v1.begin ()); i != v1.end (); ++i)
+      delete *i;
+
+    for (obj1_set::iterator i (s1.begin ()); i != s1.end (); ++i)
+      delete *i;
+
+    for (obj1_map::iterator i (m1.begin ()); i != m1.end (); ++i)
+      delete i->second;
+  }
+
+  #pragma db id auto
+  unsigned long id;
+
+  obj1* o1;
+
+  obj2_ptr o2; // std::auto_ptr or std::unique_ptr
+#ifdef HAVE_CXX11
+  obj2_vec v2;
+#else
+  // Dummy containers to get the equivalent DROP TABLE statements.
+  //
+  std::vector<int> v2;
+#endif
+
+#if defined(HAVE_CXX11) || defined(HAVE_TR1_MEMORY)
+  obj3_ptr o3;
+  comp c;
+  comp_vec cv;
+#endif
+
+  obj1_vec v1;
+  obj1_set s1;
+  obj1_map m1;
+
+  std::string str;
+
+private:
+  aggr (const aggr&);
+  aggr& operator= (const aggr&);
+};
+
+inline bool
+operator== (const aggr& x, const aggr& y)
+{
+  return
+    x.id == y.id &&
+    (x.o1 ? (y.o1 && *x.o1 == *y.o1) : !y.o1) &&
+    (x.o2.get () ? (y.o2.get () && *x.o2 == *y.o2) : !y.o2.get ()) &&
+#ifdef HAVE_CXX11
+    x.v2 == y.v2 &&
+#endif
+#if defined(HAVE_CXX11) || defined(HAVE_TR1_MEMORY)
+    (x.o3.get () ? (y.o3.get () && *x.o3 == *y.o3) : !y.o3.get ()) &&
+    x.c == y.c &&
+    x.cv == y.cv &&
+#endif
+    x.v1 == y.v1 &&
+    x.s1 == y.s1 &&
+    x.m1 == y.m1 &&
+    x.str == y.str;
+}
+
+#endif // TEST_HXX
diff --git a/common/relationship/basics/test.std b/common/relationship/basics/test.std
new file mode 100644
index 0000000..e69de29
diff --git a/common/relationship/driver.cxx b/common/relationship/driver.cxx
deleted file mode 100644
index 127a894..0000000
--- a/common/relationship/driver.cxx
+++ /dev/null
@@ -1,159 +0,0 @@
-// file      : common/relationship/driver.cxx
-// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
-// license   : GNU GPL v2; see accompanying LICENSE file
-
-// Test object relationships.
-//
-
-#include <memory>   // std::auto_ptr
-#include <cassert>
-#include <iostream>
-
-#include <odb/database.hxx>
-#include <odb/transaction.hxx>
-
-#include <common/common.hxx>
-
-#include "test.hxx"
-#include "test-odb.hxx"
-
-using namespace std;
-using namespace odb::core;
-
-int
-main (int argc, char* argv[])
-{
-  try
-  {
-    auto_ptr<database> db (create_database (argc, argv));
-
-    aggr a ("aggr");
-    a.o1 = new obj1 ("o1", "obj1");
-    a.o2.reset (new obj2 ("obj2"));
-
-#ifdef HAVE_CXX11
-    a.v2.push_back (obj2_ptr (new obj2 ("v1 obj2 1")));
-    a.v2.push_back (0);
-    a.v2.push_back (obj2_ptr (new obj2 ("v1 obj2 2")));
-#endif
-
-#if defined(HAVE_CXX11) || defined(HAVE_TR1_MEMORY)
-    a.o3.reset (new obj3 ("obj3"));
-
-    a.c.num = 123;
-    a.c.o3.reset (new obj3 ("c"));
-
-    a.cv.push_back (comp (234, obj3_ptr (new obj3 ("cv 0"))));
-    a.cv.push_back (comp (235, obj3_ptr ()));
-    a.cv.push_back (comp (236, obj3_ptr (new obj3 ("cv 2"))));
-#endif
-
-    a.v1.push_back (new obj1 ("v1 0", "v1 0"));
-    a.v1.push_back (0);
-    a.v1.push_back (new obj1 ("v1 2", "v1 2"));
-
-    // Set cannot contain NULL pointers.
-    //
-    a.s1.insert (new obj1 ("s1 0", "s1 0"));
-    a.s1.insert (new obj1 ("s1 2", "s1 2"));
-
-    a.m1[0] = new obj1 ("m1 0", "m1 0");
-    a.m1[1] = 0;
-    a.m1[2] = new obj1 ("m1 2", "m1 2");
-
-    // persist
-    //
-    {
-      transaction t (db->begin ());
-      db->persist (a.o1);
-      db->persist (a.o2);
-
-#ifdef HAVE_CXX11
-      for (obj2_vec::iterator i (a.v2.begin ()); i != a.v2.end (); ++i)
-        if (*i)
-          db->persist (*i);
-#endif
-
-#if defined(HAVE_CXX11) || defined(HAVE_TR1_MEMORY)
-      db->persist (a.o3);
-
-      db->persist (a.c.o3);
-
-      for (comp_vec::iterator i (a.cv.begin ()); i != a.cv.end (); ++i)
-        if (i->o3)
-          db->persist (i->o3);
-#endif
-
-      for (obj1_vec::iterator i (a.v1.begin ()); i != a.v1.end (); ++i)
-        if (*i)
-          db->persist (*i);
-
-      for (obj1_set::iterator i (a.s1.begin ()); i != a.s1.end (); ++i)
-        if (*i)
-          db->persist (*i);
-
-      for (obj1_map::iterator i (a.m1.begin ()); i != a.m1.end (); ++i)
-        if (i->second)
-          db->persist (i->second);
-
-      db->persist (a);
-      t.commit ();
-    }
-
-    // load & compare
-    //
-    {
-      transaction t (db->begin ());
-      auto_ptr<aggr> a1 (db->load<aggr> (a.id));
-      t.commit ();
-
-      assert (*a1 == a);
-    }
-
-    // query
-    //
-    typedef odb::query<aggr> query;
-    typedef odb::result<aggr> result;
-
-    {
-      transaction t (db->begin ());
-
-      result r (db->query<aggr> (query::o1->str == "obj1"));
-      assert (!r.empty ());
-      assert (r.begin ()->o1->id == a.o1->id);
-      assert (size (r) == 1);
-
-      t.commit ();
-    }
-
-    // Test NULL pointer.
-    //
-    delete a.o1;
-    a.o1 = 0;
-    a.o2.reset ();
-#if defined(HAVE_CXX11) || defined(HAVE_TR1_MEMORY)
-    a.o3.reset ();
-#endif
-
-    {
-      transaction t (db->begin ());
-      db->update (a);
-      t.commit ();
-    }
-
-    // load & compare
-    //
-    {
-      transaction t (db->begin ());
-      auto_ptr<aggr> a1 (db->load<aggr> (a.id));
-      t.commit ();
-
-      assert (*a1 == a);
-    }
-  }
-  catch (const odb::exception& e)
-  {
-    cerr << e.what () << endl;
-    return 1;
-  }
-}
diff --git a/common/relationship/makefile b/common/relationship/makefile
deleted file mode 100644
index 5857ca0..0000000
--- a/common/relationship/makefile
+++ /dev/null
@@ -1,115 +0,0 @@
-# file      : common/relationship/makefile
-# copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
-# license   : GNU GPL v2; see accompanying LICENSE file
-
-include $(dir $(lastword $(MAKEFILE_LIST)))../../build/bootstrap.make
-
-cxx_tun := driver.cxx
-odb_hdr := test.hxx
-genf    := $(call odb-gen,$(odb_hdr))
-gen     := $(addprefix $(out_base)/,$(genf))
-cxx_obj := $(addprefix $(out_base)/,$(cxx_tun:.cxx=.o)) $(filter %.o,$(gen:.cxx=.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
-
-# 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)
-
-$(gen): $(odb)
-$(gen): odb := $(odb)
-$(gen) $(dist): export odb_options += --generate-schema --generate-query \
---table-prefix t_relationship_
-$(gen): cpp_options := -I$(src_base)
-$(gen): $(common.l.cpp-options)
-
-ifneq ($(db_id),common)
-$(gen): odb_options += --database $(db_id)
-else
-$(gen): odb_options += --multi-database dynamic
-endif
-
-$(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): sources := $(cxx_tun)
-$(dist): headers := $(odb_hdr)
-$(dist): data_dist := test.std
-$(dist): export name := $(name)
-$(dist): export extra_dist := $(data_dist) $(call vc8projs,$(name)) \
-$(call vc9projs,$(name)) $(call vc10projs,$(name)) $(call vc11projs,$(name))
-$(dist):
-	$(call dist-data,$(sources) $(headers) $(data_dist))
-	$(call meta-automake,../template/Makefile.am)
-	$(call meta-vc8projs,../template/template,$(name))
-	$(call meta-vc9projs,../template/template,$(name))
-	$(call meta-vc10projs,../template/template,$(name))
-	$(call meta-vc11projs,../template/template,$(name))
-
-# Test.
-#
-ifneq ($(db_id),common)
-$(eval $(call test-rule))
-else
-$(foreach d,$(databases),$(eval $(call test-rule,$d)))
-endif
-
-# Clean.
-#
-$(clean):                            \
-  $(driver).o.clean                  \
-  $(addsuffix .cxx.clean,$(cxx_obj)) \
-  $(addsuffix .cxx.clean,$(cxx_od))  \
-  $(addsuffix .hxx.clean,$(filter %.cxx,$(gen)))
-	$(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/vc8proj.make)
-$(call include,$(bld_root)/meta/vc9proj.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/relationship/on-delete/driver.cxx b/common/relationship/on-delete/driver.cxx
new file mode 100644
index 0000000..25b534b
--- /dev/null
+++ b/common/relationship/on-delete/driver.cxx
@@ -0,0 +1,81 @@
+// file      : common/relationship/on-delete/driver.cxx
+// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
+// license   : GNU GPL v2; see accompanying LICENSE file
+
+// Test ON DELETE functionality.
+//
+
+#include <memory>   // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+  try
+  {
+    auto_ptr<database> db (create_database (argc, argv));
+
+    object o;
+
+    cascade c;
+    c.p = &o;
+
+    cascade_cont cc;
+    cc.p.push_back (&o);
+
+    set_null n;
+    n.p = &o;
+
+    set_null_cont nc;
+    nc.p.push_back (&o);
+
+    {
+      transaction t (db->begin ());
+      db->persist (o);
+      db->persist (c);
+      db->persist (cc);
+      db->persist (n);
+      db->persist (nc);
+      t.commit ();
+    }
+
+    {
+      transaction t (db->begin ());
+      db->erase (o);
+      t.commit ();
+    }
+
+    {
+      transaction t (db->begin ());
+      assert (db->find<cascade> (c.id) == 0);
+
+      auto_ptr<cascade_cont> pcc (db->load<cascade_cont> (cc.id));
+      assert (pcc->p.empty ());
+
+      auto_ptr<set_null> pn (db->load<set_null> (n.id));
+      assert (pn->p == 0);
+
+      auto_ptr<set_null_cont> pnc (db->load<set_null_cont> (nc.id));
+      assert (pnc->p.size () == 1 && pnc->p[0] == 0);
+
+      t.commit ();
+    }
+  }
+  catch (const odb::exception& e)
+  {
+    cerr << e.what () << endl;
+    return 1;
+  }
+}
diff --git a/common/relationship/on-delete/makefile b/common/relationship/on-delete/makefile
new file mode 100644
index 0000000..bdda5be
--- /dev/null
+++ b/common/relationship/on-delete/makefile
@@ -0,0 +1,117 @@
+# file      : common/relationship/on-delete/makefile
+# copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
+# license   : GNU GPL v2; see accompanying LICENSE file
+
+include $(dir $(lastword $(MAKEFILE_LIST)))../../../build/bootstrap.make
+
+cxx_tun := driver.cxx
+odb_hdr := test.hxx
+genf    := $(call odb-gen,$(odb_hdr))
+gen     := $(addprefix $(out_base)/,$(genf))
+cxx_obj := $(addprefix $(out_base)/,$(cxx_tun:.cxx=.o)) $(filter %.o,$(gen:.cxx=.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
+
+# 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)
+
+$(gen): $(odb)
+$(gen): odb := $(odb)
+$(gen) $(dist): export odb_options += --generate-schema \
+--fkeys-deferrable-mode mysql:not_deferrable \
+--fkeys-deferrable-mode mssql:not_deferrable \
+--table-prefix t_rel_on_d_
+$(gen): cpp_options := -I$(src_base)
+$(gen): $(common.l.cpp-options)
+
+ifneq ($(db_id),common)
+$(gen): odb_options += --database $(db_id)
+else
+$(gen): odb_options += --multi-database dynamic
+endif
+
+$(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): sources := $(cxx_tun)
+$(dist): headers := $(odb_hdr)
+$(dist): data_dist := test.std
+$(dist): export name := $(name)
+$(dist): export extra_dist := $(data_dist) $(call vc8projs,$(name)) \
+$(call vc9projs,$(name)) $(call vc10projs,$(name)) $(call vc11projs,$(name))
+$(dist):
+	$(call dist-data,$(sources) $(headers) $(data_dist))
+	$(call meta-automake,../template/Makefile.am)
+	$(call meta-vc8projs,../template/template,$(name))
+	$(call meta-vc9projs,../template/template,$(name))
+	$(call meta-vc10projs,../template/template,$(name))
+	$(call meta-vc11projs,../template/template,$(name))
+
+# Test.
+#
+ifneq ($(db_id),common)
+$(eval $(call test-rule))
+else
+$(foreach d,$(databases),$(eval $(call test-rule,$d)))
+endif
+
+# Clean.
+#
+$(clean):                            \
+  $(driver).o.clean                  \
+  $(addsuffix .cxx.clean,$(cxx_obj)) \
+  $(addsuffix .cxx.clean,$(cxx_od))  \
+  $(addsuffix .hxx.clean,$(filter %.cxx,$(gen)))
+	$(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/vc8proj.make)
+$(call include,$(bld_root)/meta/vc9proj.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/relationship/on-delete/test.hxx b/common/relationship/on-delete/test.hxx
new file mode 100644
index 0000000..dba04c3
--- /dev/null
+++ b/common/relationship/on-delete/test.hxx
@@ -0,0 +1,59 @@
+// file      : common/relationship/on-delete/test.hxx
+// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
+// license   : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <vector>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+  #pragma db id auto
+  unsigned long id;
+};
+
+#pragma db object
+struct cascade
+{
+  #pragma db id auto
+  unsigned long id;
+
+  #pragma db on_delete(cascade)
+  object* p;
+};
+
+#pragma db object
+struct cascade_cont
+{
+  #pragma db id auto
+  unsigned long id;
+
+  #pragma db on_delete(cascade)
+  std::vector<object*> p;
+};
+
+#pragma db object
+struct set_null
+{
+  #pragma db id auto
+  unsigned long id;
+
+  #pragma db on_delete(set_null)
+  object* p;
+};
+
+#pragma db object
+struct set_null_cont
+{
+  #pragma db id auto
+  unsigned long id;
+
+  #pragma db on_delete(set_null)
+  std::vector<object*> p;
+};
+
+#endif // TEST_HXX
diff --git a/common/relationship/on-delete/test.std b/common/relationship/on-delete/test.std
new file mode 100644
index 0000000..e69de29
diff --git a/common/relationship/query/driver.cxx b/common/relationship/query/driver.cxx
new file mode 100644
index 0000000..2ca05f4
--- /dev/null
+++ b/common/relationship/query/driver.cxx
@@ -0,0 +1,173 @@
+// file      : common/relationship-query/query/driver.cxx
+// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
+// license   : GNU GPL v2; see accompanying LICENSE file
+
+// Test relationship queries.
+//
+
+#include <memory>   // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/session.hxx>
+#include <odb/transaction.hxx>
+
+#include <common/config.hxx> // HAVE_CXX11, HAVE_TR1_MEMORY
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+  try
+  {
+    auto_ptr<database> db (create_database (argc, argv));
+
+#if defined(HAVE_CXX11) || defined(HAVE_TR1_MEMORY)
+
+    //
+    //
+    {
+      shared_ptr<country> ca (new country ("CA", "Canada"));
+      shared_ptr<country> za (new country ("ZA", "South Africa"));
+      shared_ptr<country> us (new country ("US", "United States"));
+      shared_ptr<country> se (new country ("SE", "Sweden"));
+
+      shared_ptr<employer> st (new employer ("Simple Tech, Inc", ca));
+      shared_ptr<employer> ct (new employer ("Complex Tech, Inc", us));
+
+      // person
+      //
+      shared_ptr<person> p1 (
+        new person (1, "John", "Doe", 30, ca, true, za));
+
+      shared_ptr<person> p2 (
+        new person (2, "Jane", "Doe", 29, za, false, us));
+      p2->husband = p1;
+
+      shared_ptr<person> p3 (
+        new person (3, "Joe", "Dirt", 31,  us, true, us));
+
+      shared_ptr<person> p4 (
+        new person (4, "Johan", "Johansen", 32, se, false, ca));
+
+      // employee
+      //
+      shared_ptr<employee> e1 (
+        new employee (1, "John", "Doe", 30, ca, true, za, st));
+
+      shared_ptr<employee> e2 (
+        new employee (2, "Jane", "Doe", 29, za, false, us, ct));
+      e2->husband = p1;
+
+      shared_ptr<employee> e3 (
+        new employee (3, "Joe", "Dirt", 31,  us, true, us, st));
+
+      shared_ptr<employee> e4 (
+        new employee (4, "Johan", "Johansen", 32, se, false, ca, ct));
+
+      transaction t (db->begin ());
+      db->persist (ca);
+      db->persist (za);
+      db->persist (us);
+      db->persist (se);
+
+      db->persist (st);
+      db->persist (ct);
+
+      db->persist (p1);
+      db->persist (p2);
+      db->persist (p3);
+      db->persist (p4);
+
+      db->persist (e1);
+      db->persist (e2);
+      db->persist (e3);
+      db->persist (e4);
+      t.commit ();
+    }
+
+    typedef odb::query<person> p_query;
+    typedef odb::result<person> p_result;
+
+    typedef odb::query<employee> e_query;
+    typedef odb::result<employee> e_result;
+
+    // Make sure we have an independent JOIN for each relationship.
+    //
+    {
+      session s;
+      transaction t (db->begin ());
+
+      p_result pr (db->query<person> (
+                   p_query::residence.location->code == "ZA"));
+      assert (size (pr) == 1);
+
+      e_result er (db->query<employee> (
+                   e_query::residence.location->code == "ZA"));
+      assert (size (er) == 1);
+
+      t.commit ();
+    }
+
+    // Test Self-JOIN.
+    //
+    {
+      session s;
+      transaction t (db->begin ());
+
+      p_result pr (db->query<person> (p_query::husband->last_name == "Doe"));
+      assert (size (pr) == 1);
+
+      e_result er (db->query<employee> (e_query::husband->last_name == "Doe"));
+      assert (size (er) == 1);
+
+      t.commit ();
+    }
+
+    // Test query conditions from both base and derived.
+    //
+    {
+      session s;
+      transaction t (db->begin ());
+
+      e_result r (
+        db->query<employee> (
+          e_query::employed_by->name == "Simple Tech, Inc" &&
+          e_query::nationality->code == "US"));
+
+      assert (size (r) == 1);
+
+      t.commit ();
+    }
+
+    // Test second-level pointers.
+    //
+    {
+      session s;
+      transaction t (db->begin ());
+
+      p_result r (
+        db->query<person> (
+          p_query::husband->residence.location == "CA"));
+
+      assert (size (r) == 1);
+
+      t.commit ();
+    }
+
+#endif // HAVE_CXX11 || HAVE_TR1_MEMORY
+
+  }
+  catch (const odb::exception& e)
+  {
+    cerr << e.what () << endl;
+    return 1;
+  }
+}
diff --git a/common/relationship/query/makefile b/common/relationship/query/makefile
new file mode 100644
index 0000000..3a43749
--- /dev/null
+++ b/common/relationship/query/makefile
@@ -0,0 +1,115 @@
+# file      : common/relationship-query/query/makefile
+# copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
+# license   : GNU GPL v2; see accompanying LICENSE file
+
+include $(dir $(lastword $(MAKEFILE_LIST)))../../../build/bootstrap.make
+
+cxx_tun := driver.cxx
+odb_hdr := test.hxx
+genf    := $(call odb-gen,$(odb_hdr))
+gen     := $(addprefix $(out_base)/,$(genf))
+cxx_obj := $(addprefix $(out_base)/,$(cxx_tun:.cxx=.o)) $(filter %.o,$(gen:.cxx=.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
+
+# 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)
+
+$(gen): $(odb)
+$(gen): odb := $(odb)
+$(gen) $(dist): export odb_options += --generate-schema --generate-query \
+--generate-session --table-prefix t_rel_query_
+$(gen): cpp_options := -I$(src_base)
+$(gen): $(common.l.cpp-options)
+
+ifneq ($(db_id),common)
+$(gen): odb_options += --database $(db_id)
+else
+$(gen): odb_options += --multi-database dynamic
+endif
+
+$(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): sources := $(cxx_tun)
+$(dist): headers := $(odb_hdr)
+$(dist): data_dist := test.std
+$(dist): export name := $(name)
+$(dist): export extra_dist := $(data_dist) $(call vc8projs,$(name)) \
+$(call vc9projs,$(name)) $(call vc10projs,$(name)) $(call vc11projs,$(name))
+$(dist):
+	$(call dist-data,$(sources) $(headers) $(data_dist))
+	$(call meta-automake,../template/Makefile.am)
+	$(call meta-vc8projs,../template/template,$(name))
+	$(call meta-vc9projs,../template/template,$(name))
+	$(call meta-vc10projs,../template/template,$(name))
+	$(call meta-vc11projs,../template/template,$(name))
+
+# Test.
+#
+ifneq ($(db_id),common)
+$(eval $(call test-rule))
+else
+$(foreach d,$(databases),$(eval $(call test-rule,$d)))
+endif
+
+# Clean.
+#
+$(clean):                            \
+  $(driver).o.clean                  \
+  $(addsuffix .cxx.clean,$(cxx_obj)) \
+  $(addsuffix .cxx.clean,$(cxx_od))  \
+  $(addsuffix .hxx.clean,$(filter %.cxx,$(gen)))
+	$(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/vc8proj.make)
+$(call include,$(bld_root)/meta/vc9proj.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/relationship/query/test.hxx b/common/relationship/query/test.hxx
new file mode 100644
index 0000000..3876b27
--- /dev/null
+++ b/common/relationship/query/test.hxx
@@ -0,0 +1,151 @@
+// file      : common/relationship-query/query/test.hxx
+// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
+// license   : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <common/config.hxx> // HAVE_CXX11, HAVE_TR1_MEMORY
+
+#if defined(HAVE_CXX11) || defined(HAVE_TR1_MEMORY)
+
+#include <string>
+
+#include <odb/core.hxx>
+
+#ifdef HAVE_CXX11
+#  include <memory>
+using std::shared_ptr;
+#else
+#  include <odb/tr1/memory.hxx>
+using std::tr1::shared_ptr;
+#endif
+
+struct country;
+
+#pragma db value
+struct residence_info
+{
+  residence_info (bool p, shared_ptr<country> l)
+      : permanent (p), location (l)
+  {
+  }
+
+  residence_info ()
+  {
+  }
+
+  bool permanent;
+
+  #pragma db not_null
+  shared_ptr<country> location;
+};
+
+#pragma db object pointer(shared_ptr)
+struct person
+{
+  person (unsigned long i,
+          const std::string& fn,
+          const std::string& ln,
+          unsigned short a,
+          shared_ptr<country> r,
+          bool p,
+          shared_ptr<country> n)
+      : id (i),
+        first_name (fn),
+        last_name (ln),
+        age (a),
+        residence (p, r),
+        nationality (n)
+  {
+  }
+
+  person ()
+  {
+  }
+
+  #pragma db id
+  unsigned long id;
+
+  #pragma db column ("first")
+  std::string first_name;
+
+  #pragma db column ("last")
+  std::string last_name;
+
+  unsigned short age;
+
+  residence_info residence;
+
+  #pragma db not_null
+  shared_ptr<country> nationality;
+
+  shared_ptr<person> husband; // Self-join.
+};
+
+struct employer;
+
+#pragma db object pointer(shared_ptr)
+struct employee: person
+{
+  employee (unsigned long i,
+          const std::string& fn,
+          const std::string& ln,
+          unsigned short a,
+          shared_ptr<country> r,
+          bool p,
+          shared_ptr<country> n,
+          shared_ptr<employer> e)
+      : person (i, fn, ln, a, r, p, n),
+        employed_by (e)
+  {
+  }
+
+  employee ()
+  {
+  }
+
+  shared_ptr<employer> employed_by;
+};
+
+#pragma db object pointer(shared_ptr)
+struct employer
+{
+  employer (const std::string& n, shared_ptr<country> nat)
+      : name (n), nationality (nat)
+  {
+  }
+
+  employer ()
+  {
+  }
+
+  #pragma db id
+  std::string name;
+
+  // The same member name and type as in person (test JOIN alias).
+  //
+  #pragma db not_null
+  shared_ptr<country> nationality;
+};
+
+#pragma db object pointer(shared_ptr)
+struct country
+{
+  country (const std::string& c, std::string const& n)
+      : code (c), name (n)
+  {
+  }
+
+  country ()
+  {
+  }
+
+  #pragma db id
+  std::string code; // ISO 2-letter country code.
+
+  std::string name;
+};
+
+#endif // HAVE_CXX11 || HAVE_TR1_MEMORY
+#endif // TEST_HXX
diff --git a/common/relationship/query/test.std b/common/relationship/query/test.std
new file mode 100644
index 0000000..e69de29
diff --git a/common/relationship/test.hxx b/common/relationship/test.hxx
deleted file mode 100644
index ecfd6a3..0000000
--- a/common/relationship/test.hxx
+++ /dev/null
@@ -1,291 +0,0 @@
-// file      : common/relationship/test.hxx
-// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
-// license   : GNU GPL v2; see accompanying LICENSE file
-
-#ifndef TEST_HXX
-#define TEST_HXX
-
-#include <common/config.hxx> // HAVE_CXX11, HAVE_TR1_MEMORY
-
-#include <set>
-#include <map>
-#include <vector>
-#include <string>
-#include <memory>
-
-#include <odb/core.hxx>
-
-#if !defined(HAVE_CXX11) && defined(HAVE_TR1_MEMORY)
-#  include <odb/tr1/memory.hxx>
-#endif
-
-// Raw pointer.
-//
-#pragma db object pointer(obj1*)
-struct obj1
-{
-  obj1 () {}
-  obj1 (const std::string& i, const std::string& s): id (i), str (s) {}
-
-  #pragma db id
-  std::string id;
-  std::string str;
-};
-
-inline bool
-operator== (const obj1& x, const obj1& y)
-{
-  return x.id == y.id && x.str == y.str;
-}
-
-// vector
-//
-typedef std::vector<obj1*> obj1_vec;
-
-inline bool
-operator== (const obj1_vec& x, const obj1_vec& y)
-{
-  if (x.size () != y.size ())
-    return false;
-
-  for (obj1_vec::size_type i (0); i < x.size (); ++i)
-    if (!(x[i] ? (y[i] && *x[i] == *y[i]) : !y[i]))
-      return false;
-
-  return true;
-}
-
-// set
-//
-struct obj1_cmp
-{
-  bool
-  operator() (obj1* x, obj1* y) const
-  {
-    return (!x || !y) ? x < y : x->id < y->id;
-  }
-};
-
-typedef std::set<obj1*, obj1_cmp> obj1_set;
-
-inline bool
-operator== (const obj1_set& x, const obj1_set& y)
-{
-  if (x.size () != y.size ())
-    return false;
-
-  for (obj1_set::const_iterator i (x.begin ()); i != x.end (); ++i)
-  {
-    obj1_set::const_iterator j (y.find (*i));
-
-    if (j == y.end ())
-      return false;
-
-    obj1* x (*i);
-    obj1* y (*j);
-
-    if (!(x ? (y && *x == *y) : !y))
-      return false;
-  }
-
-  return true;
-}
-
-// map
-//
-typedef std::map<int, obj1*> obj1_map;
-
-inline bool
-operator== (const obj1_map& x, const obj1_map& y)
-{
-  if (x.size () != y.size ())
-    return false;
-
-  for (obj1_map::const_iterator i (x.begin ()); i != x.end (); ++i)
-  {
-    obj1_map::const_iterator j (y.find (i->first));
-
-    if (j == y.end ())
-      return false;
-
-    obj1* x (i->second);
-    obj1* y (j->second);
-
-    if (!(x ? (y && *x == *y) : !y))
-      return false;
-  }
-
-  return true;
-}
-
-// auto_ptr/unique_ptr
-//
-struct obj2;
-
-#ifdef HAVE_CXX11
-typedef std::unique_ptr<obj2> obj2_ptr;
-#else
-typedef std::auto_ptr<obj2> obj2_ptr;
-#endif
-
-#pragma db object pointer(obj2_ptr)
-struct obj2
-{
-  obj2 () {}
-  obj2 (const std::string& s): str (s) {}
-
-  #pragma db id auto
-  unsigned long id;
-
-  std::string str;
-};
-
-inline bool
-operator== (const obj2& x, const obj2& y)
-{
-  return x.id == y.id && x.str == y.str;
-}
-
-#ifdef HAVE_CXX11
-typedef std::vector<obj2_ptr> obj2_vec;
-
-inline bool
-operator== (const obj2_vec& x, const obj2_vec& y)
-{
-  if (x.size () != y.size ())
-    return false;
-
-  for (obj2_vec::size_type i (0); i < x.size (); ++i)
-    if (!(x[i] ? (y[i] && *x[i] == *y[i]) : !y[i]))
-      return false;
-
-  return true;
-}
-#endif
-
-// shared_ptr
-//
-#if defined(HAVE_CXX11) || defined(HAVE_TR1_MEMORY)
-struct obj3;
-
-#ifdef HAVE_CXX11
-typedef std::shared_ptr<obj3> obj3_ptr;
-#else
-typedef std::tr1::shared_ptr<obj3> obj3_ptr;
-#endif
-
-#pragma db object pointer(obj3_ptr)
-struct obj3
-{
-  obj3 () {}
-  obj3 (const std::string& s): str (s) {}
-
-  #pragma db id auto
-  unsigned long id;
-
-  std::string str;
-};
-
-inline bool
-operator== (const obj3& x, const obj3& y)
-{
-  return x.id == y.id && x.str == y.str;
-}
-
-// composite
-//
-#pragma db value
-struct comp
-{
-  comp () {}
-  comp (int n, obj3_ptr o): num (n), o3 (o) {}
-
-  int num;
-  obj3_ptr o3;
-};
-
-inline bool
-operator== (const comp& x, const comp& y)
-{
-  return x.num == y.num &&
-    (x.o3 ? (y.o3 && *x.o3 == *y.o3) : !y.o3);
-}
-
-typedef std::vector<comp> comp_vec;
-#endif
-
-//
-//
-#pragma db object
-struct aggr
-{
-  aggr (): o1 (0) {}
-  aggr (const std::string& s): o1 (0), str (s) {}
-
-  ~aggr ()
-  {
-    delete o1;
-
-    for (obj1_vec::iterator i (v1.begin ()); i != v1.end (); ++i)
-      delete *i;
-
-    for (obj1_set::iterator i (s1.begin ()); i != s1.end (); ++i)
-      delete *i;
-
-    for (obj1_map::iterator i (m1.begin ()); i != m1.end (); ++i)
-      delete i->second;
-  }
-
-  #pragma db id auto
-  unsigned long id;
-
-  obj1* o1;
-
-  obj2_ptr o2; // std::auto_ptr or std::unique_ptr
-#ifdef HAVE_CXX11
-  obj2_vec v2;
-#else
-  // Dummy containers to get the equivalent DROP TABLE statements.
-  //
-  std::vector<int> v2;
-#endif
-
-#if defined(HAVE_CXX11) || defined(HAVE_TR1_MEMORY)
-  obj3_ptr o3;
-  comp c;
-  comp_vec cv;
-#endif
-
-  obj1_vec v1;
-  obj1_set s1;
-  obj1_map m1;
-
-  std::string str;
-
-private:
-  aggr (const aggr&);
-  aggr& operator= (const aggr&);
-};
-
-inline bool
-operator== (const aggr& x, const aggr& y)
-{
-  return
-    x.id == y.id &&
-    (x.o1 ? (y.o1 && *x.o1 == *y.o1) : !y.o1) &&
-    (x.o2.get () ? (y.o2.get () && *x.o2 == *y.o2) : !y.o2.get ()) &&
-#ifdef HAVE_CXX11
-    x.v2 == y.v2 &&
-#endif
-#if defined(HAVE_CXX11) || defined(HAVE_TR1_MEMORY)
-    (x.o3.get () ? (y.o3.get () && *x.o3 == *y.o3) : !y.o3.get ()) &&
-    x.c == y.c &&
-    x.cv == y.cv &&
-#endif
-    x.v1 == y.v1 &&
-    x.s1 == y.s1 &&
-    x.m1 == y.m1 &&
-    x.str == y.str;
-}
-
-#endif // TEST_HXX
diff --git a/common/relationship/test.std b/common/relationship/test.std
deleted file mode 100644
index e69de29..0000000
-- 
cgit v1.1