// file      : common/inheritance/polymorphism/driver.cxx
// copyright : Copyright (c) 2009-2015 Code Synthesis Tools CC
// license   : GNU GPL v2; see accompanying LICENSE file

// Test polymorphic object inheritance.
//

#include <memory>   // std::auto_ptr
#include <cassert>
#include <iostream>

#include <odb/database.hxx>
#include <odb/session.hxx>
#include <odb/transaction.hxx>

#include <common/common.hxx>

#include "test1.hxx"
#include "test2.hxx"
#include "test3.hxx"
#include "test4.hxx"
#include "test5.hxx"
#include "test6.hxx"
#include "test7.hxx"
#include "test8.hxx"
#include "test9.hxx"
#include "test10.hxx"
#include "test11.hxx"
#include "test12.hxx"
#include "test13.hxx"
#include "test14.hxx"
#include "test15.hxx"

#include "test1-odb.hxx"
#include "test2-odb.hxx"
#include "test3-odb.hxx"
#include "test4-odb.hxx"
#include "test5-odb.hxx"
#include "test6-odb.hxx"
#include "test7-odb.hxx"
#include "test8-odb.hxx"
#include "test9-odb.hxx"
#include "test10-odb.hxx"
#include "test11-odb.hxx"
#include "test12-odb.hxx"
#include "test13-odb.hxx"
#include "test14-odb.hxx"
#include "test15-odb.hxx"

using namespace std;
using namespace odb::core;

const char* events[] =
{
  "pre_persist",
  "post_persist",
  "pre_load",
  "post_load",
  "pre_update",
  "post_update",
  "pre_erase",
  "post_erase"
};

namespace test6
{
  void base::
  db_callback (callback_event e, database&)
  {
    cout << "base " << events[e] << " " << id << endl;
  }

  void base::
  db_callback (callback_event e, database&) const
  {
    cout << "base " << events[e] << " " << id << " const" << endl;
  }

  void derived::
  db_callback (callback_event e, database&) const
  {
    cout << "derived " << events[e] << " " << id << " const" << endl;
  }
}

int
main (int argc, char* argv[])
{
  try
  {
    auto_ptr<database> db (create_database (argc, argv));

    // Test 1: basic polymorphism functionality.
    //
    {
      using namespace test1;

      root r (1, 1);
      base b (2, 2, "bbb");
      derived d (3, 3, "ddd");

      r.strs.push_back ("a");
      r.strs.push_back ("aa");
      r.strs.push_back ("aaa");

      b.nums.push_back (21);
      b.nums.push_back (22);
      b.nums.push_back (23);
      b.strs.push_back ("b");
      b.strs.push_back ("bb");
      b.strs.push_back ("bbb");

      d.nums.push_back (31);
      d.nums.push_back (32);
      d.nums.push_back (33);
      d.strs.push_back ("d");
      d.strs.push_back ("dd");
      d.strs.push_back ("ddd");

      {
        transaction t (db->begin ());

        // Static persist.
        //
        db->persist (r);
        db->persist (b);

        // Dynamic persist.
        //
        root& r (d);
        db->persist (r);

        t.commit ();
      }

      // Static load.
      //
      {
        transaction t (db->begin ());
        auto_ptr<root> pr (db->load<root> (r.id));
        auto_ptr<base> pb (db->load<base> (b.id));
        auto_ptr<derived> pd (db->load<derived> (d.id));
        t.commit ();

        assert (*pr == r);
        assert (*pb == b);
        assert (*pd == d);
      }

      // Dynamic load.
      //
      {
        transaction t (db->begin ());
        auto_ptr<root> pb (db->load<root> (b.id));
        auto_ptr<root> pd1 (db->load<root> (d.id));
        auto_ptr<base> pd2 (db->load<base> (d.id));
        t.commit ();

        assert (*pb == b);
        assert (*pd1 == d);
        assert (*pd2 == d);
      }

      // Invalid load.
      //
      {
        transaction t (db->begin ());

        try
        {
          auto_ptr<base> p (db->load<base> (r.id));
          assert (false);
        }
        catch (const object_not_persistent&) {}

        try
        {
          auto_ptr<derived> p (db->load<derived> (b.id));
          assert (false);
        }
        catch (const object_not_persistent&) {}

        t.commit ();
      }

      // Static load into existing instance.
      //
      {
        transaction t (db->begin ());
        root r1;
        db->load (r.id, r1);
        base b1;
        db->load (b.id, b1);
        derived d1;
        db->load (d.id, d1);
        t.commit ();

        assert (r1 == r);
        assert (b1 == b);
        assert (d1 == d);
      }

      // Dynamic load into existing instance.
      //
      {
        transaction t (db->begin ());
        base b1;
        db->load (b.id, static_cast<root&> (b1));
        derived d1;
        db->load (d.id, static_cast<base&> (d1));
        t.commit ();

        assert (b1 == b);
        assert (d1 == d);
      }

      // Invalid load into existing instance.
      //
      {
        transaction t (db->begin ());

        try
        {
          base b;
          db->load (r.id, static_cast<root&> (b));
          assert (false);
        }
        catch (const object_not_persistent&) {}

        try
        {
          derived d;
          db->load (b.id, static_cast<base&> (d));
          assert (false);
        }
        catch (const object_not_persistent&) {}

        t.commit ();
      }

      // Slicing load.
      //
      {
        transaction t (db->begin ());
        root b1;
        db->load (b.id, b1);
        base d1;
        db->load (d.id, d1);
        t.commit ();

        assert (b1 == static_cast<root> (b));
        assert (d1 == static_cast<base> (d));
      }

      // Static reload.
      //
      {
        transaction t (db->begin ());
        root r1;
        r1.id = r.id;
        db->reload (r1);
        base b1;
        b1.id = b.id;
        db->reload (b1);
        derived d1;
        d1.id = d.id;
        db->reload (d1);
        t.commit ();

        assert (r1 == r);
        assert (b1 == b);
        assert (d1 == d);
      }

      // Dynamic reload.
      //
      {
        transaction t (db->begin ());
        base b1;
        b1.id = b.id;
        db->reload (static_cast<root&> (b1));
        derived d1;
        d1.id = d.id;
        db->reload (static_cast<base&> (d1));
        t.commit ();

        assert (b1 == b);
        assert (d1 == d);
      }

      // Invalid reload.
      //
      {
        transaction t (db->begin ());

        try
        {
          base b;
          b.id = r.id;
          db->reload (static_cast<root&> (b));
          assert (false);
        }
        catch (const object_not_persistent&) {}

        try
        {
          derived d;
          d.id = b.id;
          db->reload (static_cast<base&> (d));
          assert (false);
        }
        catch (const object_not_persistent&) {}

        t.commit ();
      }

      // Slicing reload.
      //
      {
        transaction t (db->begin ());
        root b1;
        b1.id = b.id;
        db->reload (b1);
        base d1;
        d1.id = d.id;
        db->reload (d1);
        t.commit ();

        assert (b1 == static_cast<root> (b));
        assert (d1 == static_cast<base> (d));
      }

      // Query.
      //
      {
        typedef odb::query<root> root_query;
        typedef odb::result<root> root_result;

        typedef odb::query<base> base_query;
        typedef odb::result<base> base_result;

        typedef odb::result<derived> derived_result;

        transaction t (db->begin ());

        // Test loading via root.
        //
        {
          root_result qr (db->query<root> ("ORDER BY" + root_query::id));
          root_result::iterator i (qr.begin ()), e (qr.end ());

          assert (i != e && *i == r);
          assert (++i != e && *i == b);
          assert (++i != e && *i == d);
          assert (++i == e);
        }

        // Test loading via base.
        //
        {
          base_result qr (db->query<base> ("ORDER BY" + base_query::id));
          base_result::iterator i (qr.begin ()), e (qr.end ());

          assert (i != e && *i == b);
          assert (++i != e && *i == d);
          assert (++i == e);
        }

        // Test loading via derived.
        //
        {
          derived_result qr (db->query<derived> ());
          derived_result::iterator i (qr.begin ()), e (qr.end ());

          assert (i != e && *i == d);
          assert (++i == e);
        }

        // Test loading into an existing instance.
        //
        {
          root_result qr (db->query<root> ());

          unsigned short mask (0);

          for (root_result::iterator i (qr.begin ()); i != qr.end (); ++i)
          {
            string ds (i.discriminator ());

            if (ds == "test1::root")
            {
              root r1;
              i.load (r1);
              assert (r1 == r);
              mask |= 1;
            }
            else if (ds == "test1::base")
            {
              base b1;
              i.load (b1);
              assert (b1 == b);
              mask |= 2;
            }
            else if (ds == "test1::derived")
            {
              derived d1;
              i.load (d1);
              assert (d1 == d);
              mask |= 4;
            }
            else
              assert (false);
          }

          assert (mask == 7);
        }

        // Test query conditions with columns from multiple tables.
        //
        {
          base_result qr (
            db->query<base> (
              base_query::num == 3 && base_query::str == "ddd"));

          base_result::iterator i (qr.begin ()), e (qr.end ());

          assert (i != e && *i == d);
          assert (++i == e);
        }

        // Test discriminator access.
        //
        {
          base_result qr (db->query<base> (base_query::id == 3));
          base_result::iterator i (qr.begin ()), e (qr.end ());

          assert (i != e && i.discriminator () == "test1::derived");
          assert (++i == e);
        }

        // Test loading of an object from the same hierarchy during
        // query result iteration (tests image copying via change
        // callbacks in some databases).
        //
        {
          base_result qr (db->query<base> ());

          unsigned short mask (0);

          for (base_result::iterator i (qr.begin ()); i != qr.end (); ++i)
          {
            string ds (i.discriminator ());

            if (ds == "test1::base")
            {
              auto_ptr<derived> d1 (db->load<derived> (d.id));
              assert (*d1 == d);
              assert (*i == b);
              mask |= 1;
            }
            else if (ds == "test1::derived")
            {
              auto_ptr<base> b1 (db->load<base> (b.id));
              assert (*b1 == b);
              assert (*i == d);
              mask |= 2;
            }
          }

          assert (mask == 3);
        }

        t.commit ();
      }

      // Views.
      //
      {
        typedef odb::query<root_view> root_query;
        typedef odb::result<root_view> root_result;

        typedef odb::query<base_view> base_query;
        typedef odb::result<base_view> base_result;

        typedef odb::result<derived_view> derived_result;

        transaction t (db->begin ());

        // root
        //
        {
          root_result qr (db->query<root_view> ("ORDER BY" + root_query::id));
          root_result::iterator i (qr.begin ()), e (qr.end ());

          assert (i != e && i->typeid_ == "test1::root" && i->num == r.num);
          assert (++i != e && i->typeid_ == "test1::base" && i->num == b.num);
          assert (++i != e && i->typeid_ == "test1::derived" && i->num == d.num);
          assert (++i == e);
        }

        // base
        //
        {
          base_result qr (db->query<base_view> ("ORDER BY" + base_query::id));
          base_result::iterator i (qr.begin ()), e (qr.end ());

          assert (i != e &&
                  i->id == b.id && i->num == b.num && i->str == b.str);
          assert (++i != e &&
                  i->id == d.id && i->num == d.num && i->str == d.str);
          assert (++i == e);
        }

        // derived
        //
        {
          derived_result qr (db->query<derived_view> ());
          derived_result::iterator i (qr.begin ()), e (qr.end ());

          assert (i != e &&
                  i->num == d.num && i->str == d.str &&
                  i->dnum == d.dnum && i->dstr == d.dstr);
          assert (++i == e);
        }

        t.commit ();
      }

      // Update.
      //
      r.num++;
      r.strs.push_back ("aaaa");

      b.num++;
      b.str += "b";
      b.nums.push_back (24);
      b.strs.push_back ("bbbb");

      d.num++;
      d.str += "d";
      d.dnum++;
      d.dstr += "d";
      d.nums.push_back (34);
      d.strs.push_back ("dddd");

      {
        transaction t (db->begin ());

        // Static update.
        //
        db->update (r);
        db->update (b);

        // Dynamic update.
        //
        root& r (d);
        db->update (r);

        t.commit ();
      }

      // Verify update.
      //
      {
        transaction t (db->begin ());
        auto_ptr<root> pr (db->load<root> (r.id));
        auto_ptr<base> pb (db->load<base> (b.id));
        auto_ptr<derived> pd (db->load<derived> (d.id));
        t.commit ();

        assert (*pr == r);
        assert (*pb == b);
        assert (*pd == d);
      }

      // Invalid erase via id.
      //
      {
        transaction t (db->begin ());

        try
        {
          db->erase<base> (r.id);
          assert (false);
        }
        catch (const object_not_persistent&) {}

        try
        {
          db->erase<derived> (b.id);
          assert (false);
        }
        catch (const object_not_persistent&) {}

        t.commit ();
      }

      // Static erase via id.
      //
      {
        transaction t (db->begin ());
        db->erase<root> (r.id);
        db->erase<base> (b.id);
        t.commit ();
      }

      // Dynamic erase via id.
      //
      {
        transaction t (db->begin ());
        db->erase<root> (d.id);
        t.commit ();
      }

      {
        transaction t (db->begin ());
        db->persist (r);
        db->persist (b);
        db->persist (d);
        t.commit ();
      }

      // Static erase via object.
      //
      {
        transaction t (db->begin ());
        db->erase (r);
        db->erase (b);
        t.commit ();
      }

      // Dynamic erase via object.
      //
      {
        const root& r (d);
        transaction t (db->begin ());
        db->erase (r);
        t.commit ();
      }
    }

    // Test 2: inverse object pointers in polymorhic bases.
    //
    {
      using namespace test2;

      derived d (1, "d", 1);
      root_pointer rp (&d);
      base_pointer bp (&d);

      {
        transaction t (db->begin ());
        db->persist (rp);
        db->persist (bp);

        d.rp.reset (*db, &rp);
        d.bp.reset (*db, &bp);

        db->persist (d);
        t.commit ();
      }

      {
        transaction t (db->begin ());
        auto_ptr<derived> pd (db->load<derived> (d.id));
        auto_ptr<base> pb (db->load<base> (d.id));
        auto_ptr<root> pr (db->load<root> (d.id));
        t.commit ();

        assert (pd->rp.object_id<root_pointer> () == rp.id &&
                pd->bp.object_id<base_pointer> () == bp.id);

        derived* p (dynamic_cast<derived*> (pb.get ()));
        assert (p != 0 &&
                p->rp.object_id<root_pointer> () == rp.id &&
                p->bp.object_id<base_pointer> () == bp.id);

        p = dynamic_cast<derived*> (pr.get ());
        assert (p != 0 &&
                p->rp.object_id<root_pointer> () == rp.id &&
                p->bp.object_id<base_pointer> () == bp.id);
      }

      // Query.
      //
      {
        typedef odb::query<base> base_query;
        typedef odb::result<base> base_result;

        transaction t (db->begin ());

        // Test query conditions with columns in pointed-to objects from
        // multiple tables.
        //
        {
          base_result qr (
            db->query<base> (
              base_query::rp->id == rp.id &&
              base_query::bp->id == bp.id));

          base_result::iterator i (qr.begin ()), e (qr.end ());

          assert (i != e && i.discriminator () == "test2::derived");
          assert (++i == e);
        }

        t.commit ();
      }

      // Views.
      //
      {
        typedef odb::result<root_view> root_result;
        typedef odb::result<base_view> base_result;

        transaction t (db->begin ());

        // root
        //
        {
          root_result qr (db->query<root_view> ());
          root_result::iterator i (qr.begin ()), e (qr.end ());

          assert (i != e && i->rp_id == rp.id && i->r_id == d.id);
          assert (++i == e);
        }

        // base
        //
        {
          base_result qr (db->query<base_view> ());
          base_result::iterator i (qr.begin ()), e (qr.end ());

          assert (i != e &&
                  i->bp_id == bp.id && i->b_id == d.id && i->str == d.str);
          assert (++i == e);
        }

        t.commit ();
      }
    }

    // Test 3: delayed loading.
    //
    {
      using namespace test3;

      base b1 (21, 21);
      base b2 (22, 22);
      base b3 (23, 23);

      derived d1 (31, 31, "d");
      derived d2 (32, 32, "dd");
      derived d3 (33, 33, "ddd");

      b1.rptr = new root (1);
      b2.rptr = new base (2, 2);
      b3.rptr = new derived (3, 3, "b3");

      d1.rptr = new root (4);
      d2.rptr = new base (5, 5);
      d3.rptr = new derived (6, 6, "d3");

      d2.bptr = new base (7, 7);
      d3.bptr = new derived (8, 8, "d3b");

      {
        transaction t (db->begin ());
        db->persist (b1);
        db->persist (b2);
        db->persist (b3);

        db->persist (d1);
        db->persist (d2);
        db->persist (d3);

        db->persist (b1.rptr);
        db->persist (b2.rptr);
        db->persist (b3.rptr);

        db->persist (d1.rptr);
        db->persist (d2.rptr);
        db->persist (d3.rptr);

        db->persist (d2.bptr);
        db->persist (d3.bptr);

        t.commit ();
      }

      {
        transaction t (db->begin ());

        {
          auto_ptr<base> p1 (db->load<base> (b1.id));
          auto_ptr<base> p2 (db->load<base> (b2.id));
          auto_ptr<root> p3 (db->load<root> (b3.id));
          assert (*p1 == b1);
          assert (*p2 == b2);
          assert (*p3 == b3);
        }

        {
          auto_ptr<derived> p1 (db->load<derived> (d1.id));
          auto_ptr<base> p2 (db->load<base> (d2.id));
          auto_ptr<root> p3 (db->load<root> (d3.id));
          assert (*p1 == d1);
          assert (*p2 == d2);
          assert (*p3 == d3);
        }

        t.commit ();
      }

      // Query.
      //
      {
        typedef odb::query<derived> derived_query;
        typedef odb::result<derived> derived_result;

        transaction t (db->begin ());

        // Test query conditions with columns in pointed-to objects from
        // multiple tables.
        //
        {
          derived_result qr (
            db->query<derived> (
              derived_query::rptr->id == 6 &&
              derived_query::bptr->id == 8 &&
              derived_query::bptr->num == 8));

          derived_result::iterator i (qr.begin ()), e (qr.end ());

          assert (i != e && *i == d3);
          assert (++i == e);
        }

        t.commit ();
      }

      // Views.
      //
      {
        typedef odb::query<base_view> base_query;
        typedef odb::result<base_view> base_result;

        typedef odb::query<derived_view> derived_query;
        typedef odb::result<derived_view> derived_result;

        typedef odb::query<root_view> root_query;
        typedef odb::result<root_view> root_result;

        transaction t (db->begin ());

        // base
        //
        {
          base_result qr (
            db->query<base_view> (
              base_query::base::num == b2.num &&
              base_query::base::id == b2.id &&
              base_query::r::id == b2.rptr->id));

          base_result::iterator i (qr.begin ()), e (qr.end ());

          assert (i != e &&
                  i->b_id == b2.id &&
                  i->r_id == b2.rptr->id &&
                  i->num == b2.num);
          assert (++i == e);
        }

        // derived
        //
        {
          derived_result qr (
            db->query<derived_view> (
              derived_query::d::str == d3.str &&
              derived_query::d::num == d3.num &&
              derived_query::b::num == d3.bptr->num &&
              derived_query::d::id == d3.id &&
              derived_query::b::id == d3.bptr->id &&
              derived_query::r::id == d3.rptr->id));

          derived_result::iterator i (qr.begin ()), e (qr.end ());

          assert (i != e &&
                  i->d_id == d3.id &&
                  i->b_id == d3.bptr->id &&
                  i->r_id == d3.rptr->id &&
                  i->d_num == d3.num &&
                  i->b_num == d3.bptr->num &&
                  i->str == d3.str);
          assert (++i == e);
        }

        // root
        //
        {
          root_result qr (
            db->query<root_view> (
              root_query::r::id.in (b2.rptr->id, d2.rptr->id)));

          root_result::iterator i (qr.begin ()), e (qr.end ());

          assert (i != e &&
                  i->r_id == d2.rptr->id &&
                  i->d_id == d2.id &&
                  i->str == d2.str);
          assert (++i == e);
        }

        t.commit ();
      }
    }

    // Test 4: views.
    //
    {
      using namespace test4;

      base1 b1 (21, 1);

      root2 r2 (11, 0);
      base2 b2 (21, 1, "abc");

      {
        transaction t (db->begin ());
        db->persist (b1);
        db->persist (r2);
        db->persist (b2);
        t.commit ();
      }

      {
        typedef odb::query<view1> query;
        typedef odb::result<view1> result;

        transaction t (db->begin ());

        {
          result qr (
            db->query<view1> (
              query::base1::num == b1.num));

          result::iterator i (qr.begin ()), e (qr.end ());

          assert (i != e && i->str == "abc");
          assert (++i == e);
        }

        t.commit ();
      }

      {
        typedef odb::result<view2> result;

        transaction t (db->begin ());

        {
          result qr (db->query<view2> ());
          result::iterator i (qr.begin ()), e (qr.end ());

          assert (i != e && i->min_num == 1);
          assert (++i == e);
        }

        t.commit ();
      }

      {
        typedef odb::result<view3> result;

        transaction t (db->begin ());

        {
          result qr (db->query<view3> ());
          result::iterator i (qr.begin ()), e (qr.end ());

          assert (i != e && i->str == "abc");
          assert (++i == e);
        }

        t.commit ();
      }
    }

    // Test 5: polymorphism and optimistic concurrency.
    //
    {
      using namespace test5;

      root r (1, 1);
      base b (2, 2, "bbb");
      derived d (3, 3, "ddd");

      r.strs.push_back ("a");
      r.strs.push_back ("aa");
      r.strs.push_back ("aaa");

      b.nums.push_back (21);
      b.nums.push_back (22);
      b.nums.push_back (23);
      b.strs.push_back ("b");
      b.strs.push_back ("bb");
      b.strs.push_back ("bbb");

      d.nums.push_back (31);
      d.nums.push_back (32);
      d.nums.push_back (33);
      d.strs.push_back ("d");
      d.strs.push_back ("dd");
      d.strs.push_back ("ddd");

      {
        transaction t (db->begin ());
        db->persist (r);
        db->persist (b);
        db->persist (d);
        t.commit ();
      }

      // Update.
      //
      {
        transaction t (db->begin ());

        // Root.
        //
        {
          auto_ptr<root> p (db->load<root> (r.id));

          r.num++;
          r.strs.push_back ("aaaa");
          db->update (r);

          p->num--;
          p->strs.pop_back ();
          try
          {
            db->update (p);
            assert (false);
          }
          catch (const odb::object_changed&) {}

          // Make sure the object is intact.
          //
          db->reload (p);
          assert (r == *p);
        }

        // Base.
        //
        {
          auto_ptr<base> p (db->load<base> (b.id));

          b.num++;
          b.str += "b";
          b.strs.push_back ("bbbb");
          b.nums.push_back (24);
          db->update (b);

          p->num--;
          p->str += "B";
          p->strs.pop_back ();
          p->nums.pop_back ();
          try
          {
            db->update (p);
            assert (false);
          }
          catch (const odb::object_changed&) {}

          // Make sure the object is intact.
          //
          db->reload (p);
          assert (b == *p);
        }

        // Derived.
        //
        {
          auto_ptr<root> p (db->load<root> (d.id)); // Via root.

          d.num++;
          d.str += "d";
          d.strs.push_back ("dddd");
          d.nums.push_back (24);
          d.dnum++;
          d.dstr += "d";
          db->update (d);

          derived& d1 (static_cast<derived&> (*p));
          d1.num--;
          d1.str += "D";
          d1.strs.pop_back ();
          d1.nums.pop_back ();
          d1.dnum--;
          d1.dstr += "D";
          try
          {
            db->update (p);
            assert (false);
          }
          catch (const odb::object_changed&) {}

          // Make sure the object is intact.
          //
          db->reload (p);
          assert (d == *p);
        }

        t.commit ();
      }

      // Reload.
      //
      {
        transaction t (db->begin ());

        // Make sure reload doesn't modify the object if the versions
        // match.
        //
        derived d1 (d);
        d1.num++;
        d1.str += "d";
        d1.strs.push_back ("dddd");
        d1.nums.push_back (24);
        d1.dnum++;
        d1.dstr += "d";
        derived d2 (d1);

        db->reload (d1);
        assert (d1 == d2);

        t.commit ();
      }

      // Erase.
      //
      {
        transaction t (db->begin ());

        // Root.
        //
        {
          auto_ptr<root> p (db->load<root> (r.id));

          r.num++;
          r.strs.push_back ("aaaaa");
          db->update (r);

          try
          {
            db->erase (p);
            assert (false);
          }
          catch (const odb::object_changed&) {}

          db->reload (p);
          db->erase (p);
        }

        // Base.
        //
        {
          auto_ptr<base> p (db->load<base> (b.id));

          b.num++;
          b.str += "b";
          b.strs.push_back ("bbbb");
          b.nums.push_back (24);
          db->update (b);

          try
          {
            db->erase (p);
            assert (false);
          }
          catch (const odb::object_changed&) {}

          db->reload (p);
          db->erase (p);
        }

        // Derived.
        //
        {
          auto_ptr<root> p (db->load<root> (d.id)); // Via root.

          d.num++;
          d.str += "d";
          d.strs.push_back ("dddd");
          d.nums.push_back (24);
          d.dnum++;
          d.dstr += "d";
          db->update (d);

          try
          {
            db->erase (p);
            assert (false);
          }
          catch (const odb::object_changed&) {}

          db->reload (p);
          db->erase (p);
        }

        // Try to update non-existent object.
        //
        {
          try
          {
            db->update (d);
            assert (false);
          }
          catch (const odb::object_changed&) {}
        }

        // Try to erase non-existent object.
        //
        {
          try
          {
            db->erase (d);
            assert (false);
          }
          catch (const odb::object_changed&) {}
        }

        t.commit ();
      }
    }

    // Test 6: polymorphism and callbacks.
    //
    {
      using namespace test6;

      base b (1, 1, "bbb");
      auto_ptr<base> d (new derived (2, 2, "ddd"));

      // Persist.
      //
      {
        transaction t (db->begin ());
        db->persist (b);
        db->persist (d);
        t.commit ();
      }

      // Load.
      //
      {
        transaction t (db->begin ());

        auto_ptr<base> pb (db->load<base> (b.id));
        auto_ptr<root> pd (db->load<root> (d->id));

        db->load (b.id, *pb);
        db->load (d->id, *pd);

        db->reload (*pb);
        db->reload (*pd);

        t.commit ();
      }

      // Update.
      //
      {
        b.num++;
        d->num++;

        transaction t (db->begin ());
        db->update (b);
        db->update (d);
        t.commit ();
      }

      // Query.
      //
      {
        typedef odb::query<base> query;
        typedef odb::result<base> result;

        transaction t (db->begin ());

        result r (db->query<base> ("ORDER BY" + query::id));
        for (result::iterator i (r.begin ()); i != r.end (); ++i)
          *i;

        t.commit ();
      }

      // Erase.
      //
      {
        transaction t (db->begin ());
        db->erase (b);
        db->erase (d);
        t.commit ();
      }

      // Recursive (delayed) loading.
      //
      {
        derived d (3, 3, "dddd");
        d.ptr.reset (new derived (4, 4, "ddddd"));

        {
          transaction t (db->begin ());
          db->persist (d);
          db->persist (d.ptr);
          t.commit ();
        }

        {
          transaction t (db->begin ());
          auto_ptr<root> p (db->load<root> (d.id));
          t.commit ();
        }
      }
    }

    // Test 7: polymorphism and object cache (session).
    //
#if defined(HAVE_CXX11) || defined(HAVE_TR1_MEMORY)
    {
      using namespace test7;

      shared_ptr<root> r (new root (1, 1));
      shared_ptr<base> b (new base (2, 2, "b"));
      shared_ptr<root> d (new derived (3, 3, "d"));

      // Persist.
      //
      {
        session s;

        {
          transaction t (db->begin ());
          db->persist (r);
          db->persist (b);
          db->persist (d);
          t.commit ();
        }

        assert (db->load<root> (r->id) == r);
        assert (db->load<base> (b->id) == b);
        assert (db->load<root> (d->id) == d);
      }

      // Load.
      //
      {
        session s;

        transaction t (db->begin ());
        shared_ptr<root> r1 (db->load<root> (r->id));
        shared_ptr<base> b1 (db->load<base> (b->id));
        shared_ptr<derived> d1 (db->load<derived> (d->id));
        t.commit ();

        assert (db->load<root> (r->id) == r1);
        assert (db->load<base> (b->id) == b1);
        assert (db->load<root> (d->id) == d1);

        assert (!db->find<derived> (b->id));
      }

      // Query.
      //
      {
        typedef odb::query<root> query;
        typedef odb::result<root> result;

        session s;

        transaction t (db->begin ());
        shared_ptr<root> r1 (db->load<root> (r->id));
        shared_ptr<base> b1 (db->load<base> (b->id));
        shared_ptr<derived> d1 (db->load<derived> (d->id));
        t.commit ();

        {
          transaction t (db->begin ());

          result r (db->query<root> ("ORDER BY" + query::id));
          result::iterator i (r.begin ()), e (r.end ());

          assert (i != e && i.load () == r1);
          assert (++i != e && i.load () == b1);
          assert (++i != e && i.load () == d1);
          assert (++i == e);

          t.commit ();
        }
      }

      // Erase.
      //
      {
        session s;

        {
          transaction t (db->begin ());
          db->load<root> (r->id);
          db->load<root> (b->id);
          db->load<root> (d->id);
          t.commit ();
        }

        {
          transaction t (db->begin ());
          db->erase (r);
          db->erase (b);
          db->erase (d);
          t.commit ();
        }

        {
          transaction t (db->begin ());
          assert (!db->find<root> (r->id));
          assert (!db->find<base> (b->id));
          assert (!db->find<root> (d->id));
          t.commit ();
        }
      }
    }
#endif

    // Test 8: polymorphism and abstract bases.
    //
    {
      using namespace test8;

      base b (1, 1, "b");
      interm i (2, 2, "i", true);
      derived1 d1 (3, 3, "d1", true);
      derived2 d2 (4, 4, "d2", false);

      // Persist.
      //
      {
        transaction t (db->begin ());
        db->persist (b);
        db->persist (static_cast<root&> (d1));
        db->persist (static_cast<interm&> (d2));

        try
        {
          db->persist (i);
          assert (false);
        }
        catch (const odb::abstract_class&) {}

        try
        {
          db->persist (static_cast<base&> (i));
          assert (false);
        }
        catch (const odb::no_type_info&) {}

        t.commit ();
      }

      // Load.
      //
      {
        base vb;
        interm vi;
        derived1 vd1;
        derived2 vd2;

        transaction t (db->begin ());

        // load (id)
        //
        auto_ptr<root> pb (db->load<root> (b.id));
        auto_ptr<interm> pd1 (db->load<interm> (d1.id));
        auto_ptr<derived2> pd2 (db->load<derived2> (d2.id));

        assert (*pb == b);
        assert (*pd1 == d1);
        assert (*pd2 == d2);

        // load (id, obj)
        //
        db->load (b.id, static_cast<root&> (vb));
        db->load (d1.id, static_cast<base&> (vd1));
        db->load (d2.id, static_cast<interm&> (vd2));

        assert (vb == b);
        assert (vd1 == d1);
        assert (vd2 == d2);

        try
        {
          db->load (i.id, static_cast<root&> (vi));
          assert (false);
        }
        catch (const odb::no_type_info&) {}

        // reload (obj)
        //
        vb.num = 0;
        vd1.num = 0;
        vd2.num = 0;

        db->reload (static_cast<root&> (vb));
        db->reload (static_cast<base&> (vd1));
        db->reload (static_cast<interm&> (vd2));

        assert (vb == b);
        assert (vd1 == d1);
        assert (vd2 == d2);

        try
        {
          db->reload (static_cast<root&> (vi));
          assert (false);
        }
        catch (const odb::no_type_info&) {}

        t.commit ();
      }

      // Update.
      //
      {
        b.num++;
        b.str += 'b';
        d1.num++;
        d1.str += "d1";
        d2.num++;
        d2.str += "d1";

        {
          transaction t (db->begin ());
          db->update (static_cast<root&> (b));
          db->update (d1);
          db->update (static_cast<interm&> (d2));

          try
          {
            db->update (i);
            assert (false);
          }
          catch (const odb::abstract_class&) {}

          try
          {
            db->update (static_cast<base&> (i));
            assert (false);
          }
          catch (const odb::no_type_info&) {}

          t.commit ();
        }

        {
          transaction t (db->begin ());
          auto_ptr<base> pb (db->load<base> (b.id));
          auto_ptr<root> pd1 (db->load<root> (d1.id));
          auto_ptr<base> pd2 (db->load<base> (d2.id));
          t.commit ();

          assert (*pb == b);
          assert (*pd1 == d1);
          assert (*pd2 == d2);
        }
      }

      // Erase.
      //
      {
        transaction t (db->begin ());
        db->erase (b);
        db->erase<interm> (d1.id);
        db->erase (static_cast<root&> (d2));

        try
        {
          db->erase (i);
          assert (false);
        }
        catch (const odb::abstract_class&) {}

        try
        {
          db->erase (static_cast<base&> (i));
          assert (false);
        }
        catch (const odb::no_type_info&) {}

        t.commit ();
      }
    }

    // Test 9: polymorphism and readonly classes.
    //
    {
      using namespace test9;

      ro_root ro_r (1, 1);
      rw_base rw_b (2, 2, "b");
      ro_derived ro_d (3, 3, "d");

      rw_root rw_r (1, 1);
      ro_base ro_b (2, 2, "b");
      rw_derived rw_d (3, 3, "d");

      // Persist.
      //
      {
        transaction t (db->begin ());
        db->persist (ro_r);
        db->persist (rw_b);
        db->persist (ro_d);

        db->persist (rw_r);
        db->persist (ro_b);
        db->persist (rw_d);
        t.commit ();
      }

      // Update.
      //
      {
        ro_root ro_r1 (ro_r);
        rw_base rw_b1 (rw_b);
        ro_derived ro_d1 (ro_d);

        ro_base ro_b1 (ro_b);
        rw_derived rw_d1 (rw_d);

        ro_r1.num++;
        ro_r1.strs.push_back ("b");

        rw_b1.num++;
        rw_b1.strs.push_back ("b");
        rw_b1.str += "b";
        rw_b1.nums.push_back (2);
        rw_b.str += "b";
        rw_b.nums.push_back (2);

        ro_d1.num++;
        ro_d1.strs.push_back ("d");
        ro_d1.str += "d";
        ro_d1.nums.push_back (3);
        ro_d1.dnum++;
        ro_d1.dstr += "d";

        rw_r.num++;
        rw_r.strs.push_back ("b");

        ro_b1.num++;
        ro_b1.strs.push_back ("b");
        ro_b1.str += "b";
        ro_b1.nums.push_back (2);

        rw_d1.num++;
        rw_d1.strs.push_back ("d");
        rw_d1.str += "d";
        rw_d1.nums.push_back (3);
        rw_d1.dnum++;
        rw_d1.dstr += "d";
        rw_d.dnum++;
        rw_d.dstr += "d";

        {
          // These should be no-ops.
          //
          db->update (ro_r1);
          db->update (static_cast<ro_root&> (ro_d1));
          db->update (ro_b1);

          transaction t (db->begin ());
          db->update (static_cast<ro_root&> (rw_b1));
          db->update (rw_r);
          db->update (static_cast<ro_base&> (rw_d1));
          t.commit ();
        }
      }

      // Load.
      //
      {
        transaction t (db->begin ());
        auto_ptr<ro_root> p_ro_r (db->load<ro_root> (ro_r.id));
        auto_ptr<ro_root> p_rw_b (db->load<ro_root> (rw_b.id));
        auto_ptr<ro_root> p_ro_d (db->load<ro_root> (ro_d.id));

        auto_ptr<rw_root> p_rw_r (db->load<rw_root> (rw_r.id));
        auto_ptr<rw_root> p_ro_b (db->load<rw_root> (ro_b.id));
        auto_ptr<rw_root> p_rw_d (db->load<rw_root> (rw_d.id));
        t.commit ();

        assert (*p_ro_r == ro_r);
        assert (*p_rw_b == rw_b);
        assert (*p_ro_d == ro_d);

        assert (*p_rw_r == rw_r);
        assert (*p_ro_b == ro_b);
        assert (*p_rw_d == rw_d);
      }
    }

    // Test 10: empty polymorphic classes.
    //
    {
      using namespace test10;

      base b (1, 1);
      derived d (2, 2);

      // Persist.
      //
      {
        transaction t (db->begin ());
        db->persist (b);
        db->persist (static_cast<root&> (d));
        t.commit ();
      }

      // Load.
      //
      {
        transaction t (db->begin ());
        auto_ptr<root> pb (db->load<root> (b.id));
        auto_ptr<root> pd (db->load<root> (d.id));
        t.commit ();

        assert (*pb == b);
        assert (*pd == d);
      }

      // Update.
      //
      {
        b.num++;
        d.num++;

        transaction t (db->begin ());
        db->update (static_cast<root&> (b));
        db->update (d);
        t.commit ();
      }

      // Load.
      //
      {
        transaction t (db->begin ());
        auto_ptr<root> pb (db->load<root> (b.id));
        auto_ptr<root> pd (db->load<root> (d.id));
        t.commit ();

        assert (*pb == b);
        assert (*pd == d);
      }
    }

    // Test 11: reuse and polymorphic inheritance.
    //
    {
      using namespace test11;

      base b (1, 1, "b");
      derived d (2, 2, "d");

      b.strs.push_back ("b");
      b.nums.push_back (1);

      d.strs.push_back ("d");
      d.nums.push_back (1);

      // Persist.
      //
      {
        transaction t (db->begin ());
        db->persist (b);
        db->persist (static_cast<base&> (d));
        t.commit ();
      }

      // Load.
      //
      {
        transaction t (db->begin ());
        auto_ptr<base> pb (db->load<base> (b.id));
        auto_ptr<base> pd (db->load<base> (d.id));
        t.commit ();

        assert (*pb == b);
        assert (*pd == d);
      }

      // Update.
      //
      {
        b.num++;
        b.str += "b";
        b.strs.push_back ("bb");
        b.nums.push_back (2);

        d.num++;
        d.str += "d";
        d.strs.push_back ("dd");
        d.nums.push_back (2);
        d.dnum++;
        d.dstr += "d";

        transaction t (db->begin ());
        db->update (b);
        db->update (d);
        t.commit ();
      }

      // Load.
      //
      {
        transaction t (db->begin ());
        auto_ptr<base> pb (db->load<base> (b.id));
        auto_ptr<base> pd (db->load<base> (d.id));
        t.commit ();

        assert (*pb == b);
        assert (*pd == d);
      }

      // Query.
      //
      {
        typedef odb::query<base> base_query;
        typedef odb::result<base> base_result;

        typedef odb::query<derived> derived_query;
        typedef odb::result<derived> derived_result;

        transaction t (db->begin ());

        {
          base_result qr (db->query<base> (base_query::num == 2));
          base_result::iterator i (qr.begin ()), e (qr.end ());

          assert (i != e && *i == b);
          assert (++i == e);
        }

        {
          derived_result qr (db->query<derived> (derived_query::num == 3));
          derived_result::iterator i (qr.begin ()), e (qr.end ());

          assert (i != e && *i == d);
          assert (++i == e);
        }

        t.commit ();
      }
    }

    // Test 12: polymorphic objects with auto id.
    //
    {
      using namespace test12;

      base b (1);
      derived d (2);

      unsigned long id1, id2;

      // Persist.
      //
      {
        transaction t (db->begin ());
        id1 = db->persist (b);
        id2 = db->persist (static_cast<root&> (d));
        t.commit ();
      }

      // Load.
      //
      {
        transaction t (db->begin ());
        auto_ptr<root> pb (db->load<root> (id1));
        auto_ptr<root> pd (db->load<root> (id2));
        t.commit ();

        assert (*pb == b);
        assert (*pd == d);
      }
    }

    // Test 13: polymorphic derived without any non-container data members
    // (which results in an empty SELECT statement).
    //
    {
      using namespace test13;

      base b;
      b.nums.push_back (123);
      derived d;
      d.nums.push_back (123);
      d.strs.push_back ("abc");

      base1 b1;

      // Persist.
      //
      {
        transaction t (db->begin ());
        db->persist (b);
        db->persist (d);
        db->persist (b1);
        t.commit ();
      }

      // Load.
      //
      {
        transaction t (db->begin ());
        auto_ptr<root> pbr (db->load<root> (b.id));
        auto_ptr<root> pdr (db->load<root> (d.id));
        auto_ptr<base> pdb (db->load<base> (d.id));
        auto_ptr<root> pb1r (db->load<root> (b1.id));
        t.commit ();

        base& rb (static_cast<base&> (*pbr));
        derived& rd1 (static_cast<derived&> (*pdr));
        derived& rd2 (static_cast<derived&> (*pdb));
        base1 rb1 (static_cast<base1&> (*pb1r));

        assert (rb.id == b.id && rb.nums == b.nums);
        assert (rd1.id == d.id && rd1.nums == rd1.nums &&
                rd1.strs == rd1.strs);
        assert (rd2.id == d.id && rd2.nums == rd2.nums &&
                rd2.strs == rd2.strs);
        assert (rb1.id == b1.id);
      }
    }

    // Test 14: inverse pointer in polymorphic base.
    //
    {
      using namespace test14;

      derived d;
      d.num = 123;

      d.o1 = new object1;
      d.o2 = new object2;
      d.o3.push_back (new object3);
      d.o4.push_back (new object4);

      // Persist.
      //
      {
        transaction t (db->begin ());
        db->persist (d.o1);
        db->persist (d.o2);
        db->persist (d.o3[0]);
        db->persist (d.o4[0]);
        db->persist (d);
        t.commit ();
      }

      // Load.
      //
      {
        session s;

        transaction t (db->begin ());
        object1* p1 (db->load<object1> (d.o1->id));
        object2* p2 (db->load<object2> (d.o2->id));
        object3* p3 (db->load<object3> (d.o3[0]->id));
        object4* p4 (db->load<object4> (d.o4[0]->id));
        t.commit ();

        assert (p1->d->num = d.num);
        assert (p2->d[0]->num = d.num);
        assert (p3->d[0]->num = d.num);
        assert (p4->d->num = d.num);
        delete p1->d;
      }

      // Query.
      //
      {
        typedef odb::query<object1> query;
        typedef odb::result<object1> result;

        session s;
        transaction t (db->begin ());

        result r (db->query<object1> (query::d->num == d.num));
        result::iterator i (r.begin ()), e (r.end ());

        assert (i != e && i->d->num == d.num);
        delete i.load ()->d;
        assert (++i == e);
        t.commit ();
      }

      {
        typedef odb::query<object4> query;
        typedef odb::result<object4> result;

        session s;
        transaction t (db->begin ());

        result r (db->query<object4> (query::d->num == d.num));
        result::iterator i (r.begin ()), e (r.end ());

        assert (i != e && i->d->num == d.num);
        delete i.load ()->d;
        assert (++i == e);
        t.commit ();
      }
    }

    // Test 15: LOB/long data and polymorphism.
    //
    {
      using namespace test15;

      const char data[] = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B";

      derived d;
      d.blob.assign (data, data + sizeof (data));

      // Persist.
      //
      {
        transaction t (db->begin ());
        base* b (&d);
        db->persist (b);
        t.commit ();
      }

      // Load.
      //
      {
        transaction t (db->begin ());
        auto_ptr<base> pb (db->load<base> (d.id));
        t.commit ();

        derived* pd (dynamic_cast<derived*> (pb.get ()));
        assert (pd != 0 && pd->blob == d.blob);
      }

      // Query.
      //
      {
        typedef odb::result<base> result;

        transaction t (db->begin ());

        result r (db->query<base> ());
        result::iterator i (r.begin ()), e (r.end ());

        assert (i != e);

        derived* pd (dynamic_cast<derived*> (&*i));
        assert (pd != 0 && pd->blob == d.blob);

        assert (++i == e);
        t.commit ();
      }
    }
  }
  catch (const odb::exception& e)
  {
    cerr << e.what () << endl;
    return 1;
  }
}