// file      : odb/relational/source.cxx
// copyright : Copyright (c) 2009-2015 Code Synthesis Tools CC
// license   : GNU GPL v3; see accompanying LICENSE file

#include <map>

#include <odb/gcc.hxx>

#include <odb/lookup.hxx>
#include <odb/cxx-lexer.hxx>

#include <odb/relational/source.hxx>
#include <odb/relational/generate.hxx>

using namespace std;

void relational::source::class_::
traverse_object (type& c)
{
  using semantics::data_member;

  data_member* id (id_member (c));
  bool auto_id (id && auto_ (*id));
  bool base_id (id && &id->scope () != &c); // Comes from base.
  member_access* id_ma (id ? &id->get<member_access> ("get") : 0);

  data_member* opt (optimistic (c));
  member_access* opt_ma_get (opt ? &opt->get<member_access> ("get") : 0);
  member_access* opt_ma_set (opt ? &opt->get<member_access> ("set") : 0);

  type* poly_root (polymorphic (c));
  bool poly (poly_root != 0);
  bool poly_derived (poly && poly_root != &c);
  type* poly_base (poly_derived ? &polymorphic_base (c) : 0);
  size_t poly_depth (poly_derived ? polymorphic_depth (c) : 1);
  data_member* discriminator (poly ? context::discriminator (*poly_root) : 0);

  bool abst (abstract (c));
  bool reuse_abst (abst && !poly);
  bool readonly (context::readonly (c));

  bool grow (false);
  bool grow_id (false);

  if (generate_grow)
  {
    grow = context::grow (c);
    grow_id = (id ? context::grow (*id) : false) ||
      (opt ? context::grow (*opt) : false);
  }

  column_count_type const& cc (column_count (c));
  bool versioned (context::versioned (c));

  // Schema name as a string literal or empty.
  //
  string schema_name (options.schema_name ()[db]);
  if (!schema_name.empty ())
    schema_name = strlit (schema_name);

  string const& type (class_fq_name (c));
  string traits ("access::object_traits_impl< " + type + ", id_" +
                 db.string () + " >");

  user_sections& uss (c.get<user_sections> ("user-sections"));
  user_sections* buss (poly_base != 0
                       ? &poly_base->get<user_sections> ("user-sections")
                       : 0);

  os << "// " << class_name (c) << endl
     << "//" << endl
     << endl;

  object_extra (c);

  //
  // Query (abstract and concrete).
  //

  // query_columns
  //
  if (options.generate_query ())
    query_columns_type_->traverse (c);

  // Statement cache (definition).
  //
  if (!reuse_abst && id != 0)
  {
    bool sections (false);
    bool containers (has_a (c, test_container));

    os << "struct " << traits << "::extra_statement_cache_type"
       << "{";

    instance<container_cache_members> cm;
    cm->traverse (c);

    if (containers)
      os << endl;

    instance<section_cache_members> sm;
    for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
    {
      // Skip the special version update section in reuse inheritance (we
      // always treat it as abstract).
      //
      if (i->special == user_section::special_version && !poly)
        continue;

      // Generate an entry for a readonly section in optimistic
      // polymorphic root since it can be overridden and we may
      // need to update the version.
      //
      if (!i->empty () || (poly && i->optimistic ()))
      {
        sm->traverse (*i);
        sections = true;
      }
    }

    if (sections)
      os << endl;

    os << "extra_statement_cache_type (" << endl
       << db << "::connection&" << (containers || sections ? " c" : "") <<
      "," << endl
       << "image_type&" << (sections ? " im" : "") << "," << endl
       << "id_image_type&" << (sections ? " idim" : "") << "," << endl
       << db << "::binding&" << (containers || sections ? " id" : "") <<
      "," << endl
       << db << "::binding&" << (sections ? " idv" : "");

    extra_statement_cache_extra_args (containers, sections);

    os << ")";

    instance<container_cache_init_members> cim;
    cim->traverse (c);

    instance<section_cache_init_members> sim (!containers);
    for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
    {
      if (i->special == user_section::special_version && !poly)
        continue;

      if (!i->empty () || (poly && i->optimistic ()))
        sim->traverse (*i);
    }

    os << "{"
       << "}"
       << "};";
  }

  //
  // Containers (abstract and concrete).
  //

  {
    instance<container_traits> t (c);
    t->traverse (c);
  }

  //
  // Sections (abstract and concrete).
  //

  for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
  {
    instance<section_traits> t (c);
    t->traverse (*i);
  }

  //
  // Functions (abstract and concrete).
  //

  // id(), version()
  //
  if (!poly_derived && id != 0 && !base_id)
  {
    // id (id_image_type)
    //
    if (auto_id)
    {
      os << traits << "::id_type" << endl
         << traits << "::" << endl
         << "id (const id_image_type& i)"
         << "{"
         << db << "::database* db (0);"
         << "ODB_POTENTIALLY_UNUSED (db);"
         << endl
         << "id_type id;";
      init_id_value_member_id_image_->traverse (*id);
      os << "return id;"
         << "}";
    }

    // id (image)
    //
    if (options.generate_query ())
    {
      os << traits << "::id_type" << endl
         << traits << "::" << endl
         << "id (const image_type& i)"
         << "{"
         << db << "::database* db (0);"
         << "ODB_POTENTIALLY_UNUSED (db);"
         << endl
         << "id_type id;";
      init_id_value_member_->traverse (*id);
      os << "return id;"
         << "}";
    }

    // version (image)
    //
    if (opt != 0)
    {
      os << traits << "::version_type" << endl
         << traits << "::" << endl
         << "version (const image_type& i)"
         << "{"
         << "version_type v;";
      init_version_value_member_->traverse (*opt);
      os << "return v;"
         << "}";
    }
  }

  // discriminator(image)
  //
  if (poly && !poly_derived)
  {
    os << traits << "::discriminator_type" << endl
       << traits << "::" << endl
       << "discriminator (const image_type& i)"
       << "{"
       << db << "::database* db (0);"
       << "ODB_POTENTIALLY_UNUSED (db);"
       << endl
       << "discriminator_type d;";
    init_discriminator_value_member_->traverse (*discriminator);
    os << "return d;"
       << "}";
  }

  // grow ()
  //
  if (generate_grow)
  {
    os << "bool " << traits << "::" << endl
       << "grow (image_type& i," << endl
       << truncated_vector << " t";

    if (versioned)
      os << "," << endl
         << "const schema_version_migration& svm";

    if (poly_derived)
      os << "," << endl
         << "std::size_t d";

    os << ")"
       << "{"
       << "ODB_POTENTIALLY_UNUSED (i);"
       << "ODB_POTENTIALLY_UNUSED (t);";

    if (versioned)
      os << "ODB_POTENTIALLY_UNUSED (svm);";

    os << endl
       << "bool grew (false);"
       << endl;

    index_ = 0;

    if (poly_derived)
    {
      // Select column count for this class.
      //
      size_t cols (cc.total - cc.id);

      os << "// " << class_name (*poly_base) << " base" << endl
         << "//" << endl
         << "if (--d != 0)"
         << "{"
         << "if (base_traits::grow (*i.base, " <<
        "t + " << cols << "UL" <<
        (context::versioned (*poly_base) ? ", svm" : "") <<
        (poly_base != poly_root ? ", d" : "") << "))" << endl
         << "i.base->version++;"
         << "}";
    }
    else
      inherits (c, grow_base_inherits_);

    names (c, grow_member_names_);

    os << "return grew;"
       << "}";
  }

  // bind (image_type)
  //
  os << "void " << traits << "::" << endl
     << "bind (" << bind_vector << " b," << endl;

  // If we are a derived type in a polymorphic hierarchy, then
  // we get the external id binding.
  //
  if (poly_derived)
    os << "const " << bind_vector << " id," << endl
       << "std::size_t id_size," << endl;

  os << "image_type& i," << endl
     << db << "::statement_kind sk";

  if (versioned)
    os << "," << endl
       << "const schema_version_migration& svm";

  os << ")"
     << "{"
     << "ODB_POTENTIALLY_UNUSED (sk);";

  if (versioned)
    os << "ODB_POTENTIALLY_UNUSED (svm);";

  os << endl
     << "using namespace " << db << ";"
     << endl;

  if (readonly)
    os << "assert (sk != statement_update);"
       << endl;

  os << "std::size_t n (0);"
     << endl;

  if (poly_derived)
  {
    // The id reference comes first in the insert statement.
    //
    os << "// " << id->name () << endl
       << "//" << endl
       << "if (sk == statement_insert)"
       << "{"
       << "if (id != 0)" << endl
       << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
       << "n += id_size;" // Not in if for "id unchanged" optimization.
       << "}";
  }
  else
    inherits (c, bind_base_inherits_);

  names (c, bind_member_names_);

  if (poly_derived)
  {
    // The id reference comes last in the update statement.
    //
    if (!readonly)
      os << "// " << id->name () << endl
         << "//" << endl
         << "if (sk == statement_update)"
         << "{"
         << "if (id != 0)" << endl
         << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
         << "n += id_size;" // Not in if for "id unchanged" optimization.
         << "}";

    // Bind the image chain for the select statement. Seeing that
    // this is the last statement in the function, we don't care
    // about updating n.
    //
    os << "// " << class_name (*poly_base) << " base" << endl
       << "//" << endl
       << "if (sk == statement_select)" << endl
       << "base_traits::bind (b + n, ";

    if (poly_base != poly_root)
      os << "id, id_size, ";

    os << "*i.base, sk" <<
      (context::versioned (*poly_base) ? ", svm" : "") << ");";
  }

  os << "}";

  // bind (id_image_type)
  //
  if (!poly_derived && id != 0 && !base_id)
  {
    os << "void " << traits << "::" << endl
       << "bind (" << bind_vector << " b, id_image_type& i" <<
      (opt != 0 ? ", bool bv" : "") << ")"
       << "{"
       << "std::size_t n (0);";

    if (composite_wrapper (utype (*id)))
      os << db << "::statement_kind sk (" << db << "::statement_select);";

    bind_id_member_->traverse (*id);

    if (opt != 0)
    {
      os << "if (bv)"
         << "{"
         << "n += " << column_count (c).id << ";"
         << endl;

      bind_version_member_->traverse (*opt);
      os << "}";
    }

    os << "}";
  }

  // init (image, object)
  //
  os << (generate_grow ? "bool " : "void ") << traits << "::" << endl
     << "init (image_type& i," << endl
     << "const object_type& o," << endl
     << db << "::statement_kind sk";

  if (versioned)
    os << "," << endl
       << "const schema_version_migration& svm";

  os << ")"
     << "{"
     << "ODB_POTENTIALLY_UNUSED (i);"
     << "ODB_POTENTIALLY_UNUSED (o);"
     << "ODB_POTENTIALLY_UNUSED (sk);";

  if (versioned)
    os << "ODB_POTENTIALLY_UNUSED (svm);";

  os << endl
     << "using namespace " << db << ";"
     << endl;

  if (readonly)
    os << "assert (sk != statement_update);"
       << endl;

  init_image_pre (c);

  if (generate_grow)
    os << "bool grew (false);"
       << endl;

  if (!poly_derived)
    inherits (c, init_image_base_inherits_);

  names (c, init_image_member_names_);

  if (generate_grow)
    os << "return grew;";

  os << "}";

  // init (object, image)
  //
  os << "void " << traits << "::" << endl
     << "init (object_type& o," << endl
     << "const image_type& i," << endl
     << "database* db";

  if (versioned)
    os << "," << endl
       << "const schema_version_migration& svm";

  if (poly_derived)
    os << "," << endl
       << "std::size_t d";

  os << ")"
     << "{"
     << "ODB_POTENTIALLY_UNUSED (o);"
     << "ODB_POTENTIALLY_UNUSED (i);"
     << "ODB_POTENTIALLY_UNUSED (db);";

  if (versioned)
    os << "ODB_POTENTIALLY_UNUSED (svm);";

  os << endl;

  if (poly_derived)
  {
    os << "// " << class_name (*poly_base) << " base" << endl
       << "//" << endl
       << "if (--d != 0)" << endl
       << "base_traits::init (o, *i.base, db" <<
      (context::versioned (*poly_base) ? ", svm" : "") <<
      (poly_base != poly_root ? ", d" : "") << ");"
       << endl;
  }
  else
    inherits (c, init_value_base_inherits_);

  names (c, init_value_member_names_);

  os << "}";

  // init (id_image, id)
  //
  if (id != 0 && !base_id)
  {
    os << "void " << traits << "::" << endl
       << "init (id_image_type& i, const id_type& id" <<
      (opt != 0 ? ", const version_type* v" : "") << ")"
       << "{";

    if (grow_id)
      os << "bool grew (false);";

    if (composite_wrapper (utype (*id)))
      os << db << "::statement_kind sk (" << db << "::statement_select);";

    init_id_image_member_->traverse (*id);

    if (opt != 0)
    {
      // Here we rely on the fact that init_image_member
      // always wraps the statements in a block.
      //
      os << "if (v != 0)";
      init_version_image_member_->traverse (*opt);
    }

    if (grow_id)
      os << "if (grew)" << endl
         << "i.version++;";

    os << "}";
  }

  // The rest does not apply to reuse-abstract objects.
  //
  if (reuse_abst)
    return;

  //
  // Containers (concrete).
  //

  //
  // Sections (concrete).
  //

  // Polymorphic map.
  //
  if (poly)
  {
    if (!poly_derived)
      os << traits << "::map_type*" << endl
         << traits << "::map;"
         << endl;

    // Sections. Do we have any new sections or overrides?
    //
    bool sections (
      uss.count (user_sections::count_new             |
                 user_sections::count_override        |
                 user_sections::count_load            |
                 user_sections::count_update          |
                 user_sections::count_special_version |
                 (poly ? user_sections::count_optimistic : 0)) != 0);
    if (sections)
    {
      string const& fn (flat_name (type));

      size_t n (uss.count (user_sections::count_total |
                           user_sections::count_all |
                           user_sections::count_special_version));

      map<size_t, user_section*> m;
      for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
        m[i->index] = &*i;

      os << "static const "<< traits << "::" << (abst ? "abstract_" : "") <<
        "info_type::section_functions" << endl
         << "section_functions_for_" << fn << "[] ="
         << "{";

      for (size_t i (0); i != n; ++i)
      {
        os << (i != 0 ? "," : "") << "{";

        map<size_t, user_section*>::iterator j (m.find (i));

        if (j != m.end ())
        {
          user_section& s (*j->second);
          string n (public_name (*s.member));

          // load
          //
          if (s.load_empty ())
            os << "0";
          else
            os << "&odb::section_load_impl< " << type << ", id_" << db <<
              ", " << traits << "::" << n << "_traits >";

          os << "," << endl;

          // update
          //
          // Generate an entry for a readonly section in optimistic
          // polymorphic root since it can be overridden and we may
          // need to update the version.
          //
          if (s.update_empty () && !(poly && s.optimistic ()))
            os << "0";
          else
            os << "&odb::section_update_impl< " << type << ", id_" << db <<
              ", " << traits << "::" << n << "_traits >";
        }
        else
          os << "0," << endl
             << "0";

        os << "}";
      }

      os << "};";

      os << "static const "<< traits << "::" << (abst ? "abstract_" : "") <<
        "info_type::section_list" << endl
         << "section_list_for_" << fn << " ="
         << "{"
         << n << "UL," << endl
         << "section_functions_for_" << fn
         << "};";
    }

    //
    //
    os << "const " << traits << "::" << (abst ? "abstract_" : "") <<
      "info_type" << endl
       << traits << "::info (" << endl
       << "typeid (" << type << ")," << endl;

    if (poly_derived)
      os << "&object_traits_impl< " << class_fq_name (*poly_base) <<
        ", id_" << db << " >::info," << endl;
    else
      os << "0," << endl;

    // Sections.
    //
    if (sections)
      os << "&section_list_for_" << flat_name (type);
    else
      os << "0";

    if (!abst)
    {
      os << "," << endl
         << strlit (string (type, 2, string::npos)) << "," << endl
         << "&odb::create_impl< " << type << " >," << endl
         << "&odb::dispatch_impl< " << type << ", id_" << db << " >," << endl;

      if (poly_derived)
        os << "&statements_type::delayed_loader";
      else
        os << "0";
    }

    os << ");"
       << endl;

    if (!abst)
      os << "static const " << traits << "::entry_type" << endl
         << "polymorphic_entry_for_" << flat_name (type) << ";"
         << endl;
  }

  //
  // Statements.
  //
  bool update_columns (
    cc.total != cc.id + cc.inverse + cc.readonly + cc.separate_update);

  qname table (table_name (c));
  string qtable (quote_id (table));

  // persist_statement
  //
  {
    string sep (versioned ? "\n" : " ");

    statement_columns sc;
    {
      statement_kind sk (statement_insert); // Imperfect forwarding.
      instance<object_columns> ct (sk, sc);
      ct->traverse (c);
      process_statement_columns (sc, statement_insert, versioned);
    }

    bool dv (sc.empty ()); // The DEFAULT VALUES syntax.

    os << "const char " << traits << "::persist_statement[] =" << endl
       << strlit ("INSERT INTO " + qtable + sep) << endl;

    for (statement_columns::const_iterator b (sc.begin ()), i (b),
           e (sc.end ()); i != e;)
    {
      string s;

      if (i == b)
        s += '(';
      s += i->column;
      s += (++i != e ? ',' : ')');
      s += sep;

      os << strlit (s) << endl;
    }

    instance<query_parameters> qp (table);

    string extra (persist_statement_extra (c, *qp, persist_after_columns));

    if (!extra.empty ())
      os << strlit (extra + sep) << endl;

    string values;
    if (!dv)
    {
      os << strlit ("VALUES" + sep) << endl;

      values += '(';
      instance<persist_statement_params> pt (values, *qp, sep);
      pt->traverse (c);
      values += ')';
    }
    else
      values = "DEFAULT VALUES";

    extra = persist_statement_extra (c, *qp, persist_after_values);

    if (!extra.empty ())
      values += sep;

    os << strlit (values);

    if (!extra.empty ())
      os << endl
         << strlit (extra);

    os << ";"
       << endl;
  }

  // Index of the first empty SELECT statement in poly-derived
  // statement list. All subsequent statements are also empty.
  // The first statement can never be empty (contains id and
  // type_id).
  //
  size_t empty_depth (0);

  if (id != 0)
  {
    string sep (versioned ? "\n" : " ");

    instance<object_columns_list> id_cols;
    id_cols->traverse (*id);

    std::vector<size_t> find_column_counts (abst ? 1 : poly_depth);

    // find_statement
    //
    if (poly_derived)
      os << "const char* const " << traits << "::find_statements[] ="
         << "{";
    else
      os << "const char " << traits << "::find_statement[] =" << endl;

    for (size_t d (poly_depth); d != 0;)
    {
      statement_columns sc;
      {
        statement_kind sk (statement_select); // Imperfect forwarding.
        object_section* s (&main_section); // Imperfect forwarding.
        instance<object_columns> t (qtable, sk, sc, d, s);
        t->traverse (c);
        process_statement_columns (sc, statement_select, versioned);
        find_column_counts[poly_depth - d] = sc.size ();
      }

      if (sc.size () != 0)
      {
        os << strlit ("SELECT" + sep) << endl;

        for (statement_columns::const_iterator i (sc.begin ()),
               e (sc.end ()); i != e;)
        {
          string const& c (i->column);
          os << strlit (c + (++i != e ? "," : "") + sep) << endl;
        }

        os << strlit ("FROM " + qtable + sep) << endl;

        if (d != 1)
        {
          bool f (false);    // @@ (im)perfect forwarding
          size_t d1 (d - 1); //@@ (im)perfect forward.
          instance<polymorphic_object_joins> j (c, f, d1);
          j->traverse (polymorphic_base (c));

          for (strings::const_iterator i (j->begin ()); i != j->end (); ++i)
            os << strlit (*i + sep) << endl;
        }

        {
          // For the find statement, don't join section members.
          //
          bool f (false); // @@ (im)perfect forwarding
          object_section* s (&main_section); // @@ (im)perfect forwarding
          instance<object_joins> j (c, f, d, s);
          j->traverse (c);

          for (strings::const_iterator i (j->begin ()); i != j->end (); ++i)
            os << strlit (*i + sep) << endl;
        }

        string where ("WHERE ");
        instance<query_parameters> qp (table);
        for (object_columns_list::iterator b (id_cols->begin ()), i (b);
             i != id_cols->end (); ++i)
        {
          if (i != b)
            where += " AND ";

          where += qtable + "." + quote_id (i->name) + "=" +
            convert_to (qp->next (), i->type, *i->member);
        }

        os << strlit (where);
      }
      else
        os << strlit (""); // Empty SELECT statement.

      if (abst)
        break;

      if (--d != 0)
        os << "," << endl
           << endl;
    }

    if (poly_derived)
      os << "};";
    else
      os << ";"
         << endl;

    // find_column_counts
    //
    if (poly_derived)
    {
      os << "const std::size_t " << traits << "::find_column_counts[] ="
         << "{";

      for (std::vector<size_t>::iterator b (find_column_counts.begin ()),
             i (b), e (find_column_counts.end ()); i != e;)
      {
        os << *i << "UL";

        if (*i == 0 && empty_depth == 0)
          empty_depth = i - b;

        if (++i != e)
          os << ',' << endl;
      }

      os << "};";
    }

    // find_discriminator_statement
    //
    if (poly && !poly_derived)
    {
      statement_columns sc;
      {
        statement_kind sk (statement_select); // Imperfect forwarding.
        instance<object_columns> t (qtable, sk, sc);
        t->traverse (*discriminator);

        if (opt != 0)
          t->traverse (*opt);

        process_statement_columns (sc, statement_select, false);
      }

      os << "const char " << traits << "::" << endl
         << "find_discriminator_statement[] =" << endl
         << strlit ("SELECT ") << endl;

      for (statement_columns::const_iterator i (sc.begin ()),
             e (sc.end ()); i != e;)
      {
        string const& c (i->column);
        os << strlit (c + (++i != e ? ", " : " ")) << endl;
      }

      os << strlit ("FROM " + qtable + " ") << endl;

      string where ("WHERE ");
      instance<query_parameters> qp (table);
      for (object_columns_list::iterator b (id_cols->begin ()), i (b);
           i != id_cols->end (); ++i)
      {
        if (i != b)
          where += " AND ";

        where += qtable + "." + quote_id (i->name) + "=" +
          convert_to (qp->next (), i->type, *i->member);
      }

      os << strlit (where) << ";"
         << endl;
    }

    // update_statement
    //
    if (update_columns)
    {
      string sep (versioned ? "\n" : " ");

      instance<query_parameters> qp (table);

      statement_columns sc;
      {
        query_parameters* p (qp.get ()); // Imperfect forwarding.
        statement_kind sk (statement_update); // Imperfect forwarding.
        object_section* s (&main_section); // Imperfect forwarding.
        instance<object_columns> t (sk, sc, p, s);
        t->traverse (c);
        process_statement_columns (sc, statement_update, versioned);
      }

      os << "const char " << traits << "::update_statement[] =" << endl
         << strlit ("UPDATE " + qtable + sep) << endl
         << strlit ("SET" + sep) << endl;

      for (statement_columns::const_iterator i (sc.begin ()),
             e (sc.end ()); i != e;)
      {
        string const& c (i->column);
        os << strlit (c + (++i != e ? "," : "") + sep) << endl;
      }

      // This didn't work out: cannot change the identity column.
      //
      //if (sc.empty ())
      //{
      //  // We can end up with nothing to set if we need to "touch" a row
      //  // in order to increment its optimistic concurrency version. In
      //  // this case just do a dummy assignment based on the id column.
      //  //
      //  string const& c (quote_id (id_cols->begin ()->name));
      //  os << strlit (c + "=" + c) << endl;
      //}

      string extra (update_statement_extra (c));

      if (!extra.empty ())
        os << strlit (extra + sep) << endl;

      string where ("WHERE ");
      for (object_columns_list::iterator b (id_cols->begin ()), i (b);
           i != id_cols->end (); ++i)
      {
        if (i != b)
          where += " AND ";

        where += quote_id (i->name) + "=" +
          convert_to (qp->next (), i->type, *i->member);
      }

      // Top-level version column.
      //
      if (opt != 0 && !poly_derived)
      {
        where += " AND " + column_qname (*opt, column_prefix ()) + "=" +
          convert_to (qp->next (), *opt);
      }

      os << strlit (where) << ";"
         << endl;
    }

    // erase_statement
    //
    {
      instance<query_parameters> qp (table);
      os << "const char " << traits << "::erase_statement[] =" << endl
         << strlit ("DELETE FROM " + qtable + " ") << endl;

      string where ("WHERE ");
      for (object_columns_list::iterator b (id_cols->begin ()), i (b);
           i != id_cols->end (); ++i)
      {
        if (i != b)
          where += " AND ";

        where += quote_id (i->name) + "=" +
          convert_to (qp->next (), i->type, *i->member);
      }

      os << strlit (where) << ";"
         << endl;
    }

    if (opt != 0 && !poly_derived)
    {
      instance<query_parameters> qp (table);

      os << "const char " << traits << "::optimistic_erase_statement[] " <<
        "=" << endl
         << strlit ("DELETE FROM " + qtable + " ") << endl;

      string where ("WHERE ");
      for (object_columns_list::iterator b (id_cols->begin ()), i (b);
           i != id_cols->end (); ++i)
      {
        if (i != b)
          where += " AND ";

        where += quote_id (i->name) + "=" +
          convert_to (qp->next (), i->type, *i->member);
      }

      // Top-level version column.
      //
      where += " AND " + column_qname (*opt, column_prefix ()) + "=" +
        convert_to (qp->next (), *opt);

      os << strlit (where) << ";"
         << endl;
    }
  }

  // query_statement
  //
  bool query_optimize (false);
  if (options.generate_query ())
  {
    // query_statement
    //
    strings joins;
    if (poly_depth != 1)
    {
      bool t (true);             //@@ (im)perfect forwarding
      size_t d (poly_depth - 1); //@@ (im)perfect forward.
      instance<polymorphic_object_joins> j (c, t, d);
      j->traverse (polymorphic_base (c));
      joins = j->joins;
    }

    if (id != 0)
    {
      // For the query statement we also join section members so that they
      // can be used in the WHERE clause.
      //
      bool t (true); //@@ (im)perfect forwarding
      instance<object_joins> j (c, t, poly_depth);
      j->traverse (c);
      joins.insert (joins.end (), j->begin (), j->end ());
    }

    query_optimize = !joins.empty ();

    statement_columns sc;
    {
      statement_kind sk (statement_select); //@@ Imperfect forwarding.
      object_section* s (&main_section); //@@ Imperfect forwarding.
      instance<object_columns> oc (qtable, sk, sc, poly_depth, s);
      oc->traverse (c);
      process_statement_columns (
        sc, statement_select, versioned || query_optimize);
    }

    string sep (versioned || query_optimize ? "\n" : " ");

    os << "const char " << traits << "::query_statement[] =" << endl
       << strlit ("SELECT" + sep) << endl;

    for (statement_columns::const_iterator i (sc.begin ()),
           e (sc.end ()); i != e;)
    {
      string const& c (i->column);
      os << strlit (c + (++i != e ? "," : "") + sep) << endl;
    }

    string prev ("FROM " + qtable);

    for (strings::const_iterator i (joins.begin ()); i != joins.end (); ++i)
    {
      os << strlit (prev + sep) << endl;
      prev = *i;
    }

    os << strlit (prev) << ";"
       << endl;

    // erase_query_statement
    //
    os << "const char " << traits << "::erase_query_statement[] =" << endl
       << strlit ("DELETE FROM " + qtable) << ";"
       << endl;

    // table_name
    //
    os << "const char " << traits << "::table_name[] =" << endl
       << strlit (qtable) << ";" // Use quoted name.
       << endl;
  }

  // persist ()
  //
  size_t persist_containers (has_a (c, test_straight_container));
  bool persist_versioned_containers (
    persist_containers >
    has_a (c,
           test_straight_container |
           exclude_deleted | exclude_added | exclude_versioned));

  os << "void " << traits << "::" << endl
     << "persist (database& db, " << (auto_id ? "" : "const ") <<
    "object_type& obj";

  if (poly)
    os << ", bool top, bool dyn";

  os << ")"
     << "{"
     << "ODB_POTENTIALLY_UNUSED (db);";

  if (poly)
    os << "ODB_POTENTIALLY_UNUSED (top);";

  os << endl
     << "using namespace " << db << ";"
     << endl;

  if (poly)
    os << "if (dyn)" << endl
       << "{"
       << "const std::type_info& t (typeid (obj));"
       << endl
       << "if (t != info.type)"
       << "{"
       << "const info_type& pi (root_traits::map->find (t));"
       << "pi.dispatch (info_type::call_persist, db, &obj, 0);"
       << "return;"
       << "}"
       << "}";

  // If we are database-poly-abstract but not C++-abstract, then make
  // sure we are not trying to persist an instance of an abstract class.
  //
  if (abst && !c.abstract ())
    os << "if (top)" << endl
       << "throw abstract_class ();"
       << endl;

  os << db << "::connection& conn (" << endl
     << db << "::transaction::current ().connection ());"
     << "statements_type& sts (" << endl
     << "conn.statement_cache ().find_object<object_type> ());";

  if (versioned ||
      persist_versioned_containers ||
      uss.count (user_sections::count_new |
                 user_sections::count_all |
                 user_sections::count_versioned_only) != 0)
    os << "const schema_version_migration& svm (" <<
      "sts.version_migration (" << schema_name << "));";

  os << endl;

  // Call callback (pre_persist).
  //
  if (!abst) // If we are poly-abstract, then top will always be false.
  {
    if (poly)
      os << "if (top)" << endl;

    os << "callback (db," << endl
       << (auto_id ? "static_cast<const object_type&> (obj)," : "obj,") << endl
       << "callback_event::pre_persist);"
       << endl;
  }

  // Call our base if we are a derived type in a polymorphic
  // hierarchy.
  //
  if (poly_derived)
    os << "base_traits::persist (db, obj, false, false);"
       << endl;

  os << "image_type& im (sts.image ());"
     << "binding& imb (sts.insert_image_binding ());";

  if (poly_derived)
    os << "const binding& idb (sts.id_image_binding ());";

  os << endl;

  if (generate_grow)
    os << "if (";

  os << "init (im, obj, statement_insert" << (versioned ? ", svm" : "") << ")";

  if (generate_grow)
    os << ")" << endl
       << "im.version++";

  os << ";"
     << endl;

  if (!poly_derived && auto_id && insert_send_auto_id)
  {
    string const& n (id->name ());
    string var ("im." + n + (n[n.size () - 1] == '_' ? "" : "_"));
    init_auto_id (*id, var);
    os << endl;
  }

  os << "if (";

  if (poly_derived)
    os << "idb.version != sts.insert_id_binding_version () ||" << endl;

  os << "im.version != sts.insert_image_version () ||" << endl
     << "imb.version == 0)"
     << "{"
     << "bind (imb.bind, ";

  if (poly_derived)
    os << "idb.bind, idb.count, ";

  os << "im, statement_insert" << (versioned ? ", svm" : "") << ");";

  if (poly_derived)
    os << "sts.insert_id_binding_version (idb.version);";

  os << "sts.insert_image_version (im.version);"
     << "imb.version++;"
     << "}";

  // Bind id image since that's where the returned id and/or version will
  // be extracted.
  //
  if (!poly_derived)
  {
    bool bv (opt != 0 && optimistic_insert_bind_version (*opt));
    if (bv || auto_id)
    {
      os << "{"
         << "id_image_type& i (sts.id_image ());"
         << "binding& b (sts.id_image_binding ());"
         << "if (i.version != sts.id_image_version () || b.version == 0)"
         << "{"
         << "bind (b.bind, i);"
         << "sts.id_image_version (i.version);"
         << "b.version++;";
      if (opt != 0)
        os << "sts.optimistic_id_image_binding ().version++;";
      os << "}"
         << "}";
    }
  }

  os << "insert_statement& st (sts.persist_statement ());"
     << "if (!st.execute ())" << endl
     << "throw object_already_persistent ();"
     << endl;

  if (!poly_derived && auto_id)
  {
    member_access& ma (id->get<member_access> ("set"));

    if (!ma.synthesized)
      os << "// From " << location_string (ma.loc, true) << endl;

    if (ma.placeholder ())
      os << ma.translate ("obj", "id (sts.id_image ())") << ";"
         << endl;
    else
    {
      // If this member is const and we have a synthesized direct access,
      // then cast away constness. Otherwise, we assume that the user-
      // provided expression handles this.
      //
      bool cast (ma.direct () && const_type (id->type ()));
      if (cast)
        os << "const_cast< id_type& > (" << endl;

      os << ma.translate ("obj");

      if (cast)
        os << ")";

      os << " = id (sts.id_image ());"
         << endl;
    }
  }

  // Set the optimistic concurrency version in the object member.
  //
  if (opt != 0 && !poly_derived)
  {
    // If we don't have auto id, then obj is a const reference.
    //
    string obj (auto_id ? "obj" : "const_cast< object_type& > (obj)");
    string init (optimistic_version_init (*opt));

    if (!opt_ma_set->synthesized)
      os << "// From " << location_string (opt_ma_set->loc, true) << endl;

    if (opt_ma_set->placeholder ())
      os << opt_ma_set->translate (obj, init) << ";"
         << endl;
    else
    {
      // If this member is const and we have a synthesized direct access,
      // then cast away constness. Otherwise, we assume that the user-
      // provided expression handles this.
      //
      bool cast (opt_ma_set->direct () && const_type (opt->type ()));
      if (cast)
        os << "const_cast< version_type& > (" << endl;

      os << opt_ma_set->translate (obj);

      if (cast)
        os << ")";

      os << " = " << init << ";"
         << endl;
    }
  }

  // Initialize id_image and binding if we are a root of a polymorphic
  // hierarchy or if we have non-inverse containers.
  //
  if (!poly_derived && (poly || persist_containers))
  {
    // If this is a polymorphic root without containers, then we only
    // need to do this if we are not a top-level call. If we are poly-
    // abstract, then top will always be false.
    //
    if (poly && !persist_containers && !abst)
      os << "if (!top)"
         << "{";

    os << "id_image_type& i (sts.id_image ());";

    if (!id_ma->synthesized)
      os << "// From " << location_string (id_ma->loc, true) << endl;

    os << "init (i, " <<  id_ma->translate ("obj") << ");"
       << endl
       << "binding& idb (sts.id_image_binding ());"
       << "if (i.version != sts.id_image_version () || idb.version == 0)"
       << "{"
       << "bind (idb.bind, i);"
       << "sts.id_image_version (i.version);"
       << "idb.version++;";
    if (opt != 0)
      os << "sts.optimistic_id_image_binding ().version++;";
    os << "}";

    if (poly && !persist_containers && !abst)
      os << "}";
  }

  if (persist_containers)
  {
    os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
       << endl;

    instance<container_calls> t (container_calls::persist_call);
    t->traverse (c);
  }

  // Reset sections: loaded, unchanged.
  //
  for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
  {
    // Skip special sections.
    //
    if (i->special == user_section::special_version)
      continue;

    // Skip overridden sections; they have been reset by the base.
    //
    if (i->base != 0 && poly_derived)
      continue;

    data_member& m (*i->member);

    // If the section is soft- added or deleted, check the version.
    //
    unsigned long long av (added (m));
    unsigned long long dv (deleted (m));
    if (av != 0 || dv != 0)
    {
      os << "if (";

      if (av != 0)
        os << "svm >= schema_version_migration (" << av << "ULL, true)";

      if (av != 0 && dv != 0)
        os << " &&" << endl;

      if (dv != 0)
        os << "svm <= schema_version_migration (" << dv << "ULL, true)";

      os << ")" << endl;
    }

    // Section access is always by reference.
    //
    member_access& ma (m.get<member_access> ("get"));
    if (!ma.synthesized)
      os << "// From " << location_string (ma.loc, true) << endl;

    os << ma.translate ("obj") << ".reset (true, false);"
       << endl;
  }

  // Call callback (post_persist).
  //
  if (!abst) // If we are poly-abstract, then top will always be false.
  {
    if (poly)
      os << "if (top)" << endl;

    os << "callback (db," << endl
       << (auto_id ? "static_cast<const object_type&> (obj)," : "obj,") << endl
       << "callback_event::post_persist);";
  }

  os << "}";

  // persist() bulk
  //
  if (c.count ("bulk-persist"))
  {
    os << "void " << traits << "::" << endl
       << "persist (database& db," << endl
       << (auto_id ? "" : "const ") << "object_type** objs," << endl
       << "std::size_t n," << endl
       << "multiple_exceptions& mex)"
       << "{"
       << "ODB_POTENTIALLY_UNUSED (db);"
       << endl
       << "using namespace " << db << ";"
       << endl;

    os << db << "::connection& conn (" << endl
       << db << "::transaction::current ().connection ());"
       << "statements_type& sts (" << endl
       << "conn.statement_cache ().find_object<object_type> ());";

    if (versioned ||
        uss.count (user_sections::count_new |
                   user_sections::count_all |
                   user_sections::count_versioned_only) != 0)
      os << "const schema_version_migration& svm (" <<
        "sts.version_migration (" << schema_name << "));";

    os << endl;

    os << "for (std::size_t i (0); i != n; ++i)"
       << "{"
       << "const object_type& obj (*objs[i]);"
       << "callback (db, obj, callback_event::pre_persist);"
      //@@ assumption: generate_grow is false
      //@@ assumption: insert_send_auto_id is false
       << "init (sts.image (i), obj, statement_insert" <<
      (versioned ? ", svm" : "") << ");"
       << "}";

    //@@ assumption: generate_grow is false
    os << "binding& imb (sts.insert_image_binding ());"
       << "if (imb.version == 0)"
       << "{"
       << "bind (imb.bind, sts.image (), statement_insert" <<
      (versioned ? ", svm" : "") << ");"
       << "imb.version++;"
       << "}";

    // Bind id image since that's where the returned id and/or version will
    // be extracted.
    //
    bool bv (opt != 0 && optimistic_insert_bind_version (*opt));
    if (bv || auto_id)
    {
      os << "binding& idb (sts.id_image_binding ());"
        //@@ assumption: generate_grow is false
         << "if (idb.version == 0)"
         << "{"
         << "bind (idb.bind, sts.id_image ());"
         << "idb.version++;";
      if (opt != 0)
        os << "sts.optimistic_id_image_binding ().version++;";
      os << "}";
    }

    os << "insert_statement& st (sts.persist_statement ());"
       << "n = st.execute (n, mex);" // Actual number of rows attempted.
       << endl;

    os << "for (std::size_t i (0); i != n; ++i)"
       << "{"
       << "bool r (st.result (i));" // Sets current in mex.
       << endl
       << "if (mex[i] != 0)" << endl // Pending exception for this position.
       << "continue;"
       << endl
       << "if (!r)"
       << "{"
       << "mex.insert (i, object_already_persistent ());"
       << "continue;"
       << "}"
       << "if (mex.fatal ())" << endl // Don't do any extra work.
       << "continue;"
       << endl
       << (auto_id ? "" : "const ") << "object_type& obj (*objs[i]);"
       << endl;

    // Extract auto id.
    //
    if (auto_id)
    {
      member_access& ma (id->get<member_access> ("set"));

      if (!ma.synthesized)
        os << "// From " << location_string (ma.loc, true) << endl;

      if (ma.placeholder ())
        os << ma.translate ("obj", "id (sts.id_image (i))") << ";"
           << endl;
      else
      {
        // If this member is const and we have a synthesized direct access,
        // then cast away constness. Otherwise, we assume that the user-
        // provided expression handles this.
        //
        bool cast (ma.direct () && const_type (id->type ()));
        if (cast)
          os << "const_cast< id_type& > (" << endl;

        os << ma.translate ("obj");

        if (cast)
          os << ")";

        os << " = id (sts.id_image (i));"
           << endl;
      }
    }

    // Set the optimistic concurrency version.
    //
    if (opt != 0)
    {
      // If we don't have auto id, then obj is a const reference.
      //
      string obj (auto_id ? "obj" : "const_cast< object_type& > (obj)");
      string init (optimistic_version_init (*opt, true));

      if (!opt_ma_set->synthesized)
        os << "// From " << location_string (opt_ma_set->loc, true) << endl;

      if (opt_ma_set->placeholder ())
        os << opt_ma_set->translate (obj, init) << ";"
           << endl;
      else
      {
        // If this member is const and we have a synthesized direct access,
        // then cast away constness. Otherwise, we assume that the user-
        // provided expression handles this.
        //
        bool cast (opt_ma_set->direct () && const_type (opt->type ()));
        if (cast)
          os << "const_cast< version_type& > (" << endl;

        os << opt_ma_set->translate (obj);

        if (cast)
          os << ")";

        os << " = " << init << ";"
           << endl;
      }
    }

    // Reset sections: loaded, unchanged.
    //
    for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
    {
      // Skip special sections.
      //
      if (i->special == user_section::special_version)
        continue;

      data_member& m (*i->member);

      // If the section is soft- added or deleted, check the version.
      //
      unsigned long long av (added (m));
      unsigned long long dv (deleted (m));
      if (av != 0 || dv != 0)
      {
        os << "if (";

        if (av != 0)
          os << "svm >= schema_version_migration (" << av << "ULL, true)";

        if (av != 0 && dv != 0)
          os << " &&" << endl;

        if (dv != 0)
          os << "svm <= schema_version_migration (" << dv << "ULL, true)";

        os << ")" << endl;
        }

      // Section access is always by reference.
      //
      member_access& ma (m.get<member_access> ("get"));
      if (!ma.synthesized)
        os << "// From " << location_string (ma.loc, true) << endl;

      os << ma.translate ("obj") << ".reset (true, false);"
         << endl;
    }

    os << "callback (db," << endl
       << (auto_id ? "static_cast<const object_type&> (obj)," : "obj,") << endl
       << "callback_event::post_persist);"
       << "}"  // for
       << "}"; // persist ()
  }

  // update ()
  //
  if (id != 0 && (!readonly || poly))
  {
    size_t update_containers (
      has_a (c, test_readwrite_container, &main_section));

    bool update_versioned_containers (
      update_containers >
      has_a (c,
             test_readwrite_container |
             exclude_deleted | exclude_added | exclude_versioned,
             &main_section));

    // See if we have any sections that we might have to update.
    //
    bool sections (false);
    bool versioned_sections (false);
    for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
    {
      // This test will automatically skip the special version update section.
      //
      if (i->update_empty () || i->update == user_section::update_manual)
        continue;

      sections = true;

      if (added (*i->member) || deleted (*i->member))
        versioned_sections = true;

      // For change-updated sections, also check if we have any
      // versioned smart containers.
      //
      if (i->update != user_section::update_change)
        continue;

      if (has_a (c, test_smart_container, &*i) !=
          has_a (c, test_smart_container | exclude_deleted | exclude_added, &*i))
        versioned_sections = true;
    }

    os << "void " << traits << "::" << endl
       << "update (database& db, const object_type& obj";

    if (poly)
      os << ", bool top, bool dyn";

    os << ")"
       << "{"
       << "ODB_POTENTIALLY_UNUSED (db);";

    if (poly)
      os << "ODB_POTENTIALLY_UNUSED (top);";

    os << endl
       << "using namespace " << db << ";"
       << "using " << db << "::update_statement;"
       << endl;

    if (poly)
      os << "if (dyn)" << endl
         << "{"
         << "const std::type_info& t (typeid (obj));"
         << endl
         << "if (t != info.type)"
         << "{"
         << "const info_type& pi (root_traits::map->find (t));"
         << "pi.dispatch (info_type::call_update, db, &obj, 0);"
         << "return;"
         << "}"
         << "}";

    // If we are database-poly-abstract but not C++-abstract, then make
    // sure we are not trying to update an instance of an abstract class.
    //
    if (abst && !c.abstract ())
      os << "if (top)" << endl
         << "throw abstract_class ();"
         << endl;

    // If we are readonly, then there is nothing else to do.
    //
    if (!readonly)
    {
      // Call callback (pre_update).
      //
      if (!abst) // If we are poly-abstract then top will always be false.
      {
        if (poly)
          os << "if (top)" << endl;

        os << "callback (db, obj, callback_event::pre_update);"
           << endl;
      }

      bool sts (false);

      if ((versioned && update_columns) ||
          update_versioned_containers ||
          versioned_sections)
      {
        sts = true;
        os << db << "::transaction& tr (" << db <<
          "::transaction::current ());"
           << db << "::connection& conn (tr.connection ());"
           << "statements_type& sts (" << endl
           << "conn.statement_cache ().find_object<object_type> ());"
           << endl;

        os << "const schema_version_migration& svm (" <<
          "sts.version_migration (" << schema_name << "));"
           << endl;
      }

      // If we have change-updated sections that contain change-tracking
      // containers, then mark such sections as changed if any of the
      // containers were changed. Do this before calling the base so that
      // we account for the whole hierarchy before we actually start
      // updating any sections (important for optimistic concurrency
      // version).
      //
      for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
      {
        user_section& s (*i);

        if (s.update != user_section::update_change)
          continue;

        if (!has_a (c, test_smart_container, &s))
          continue;

        data_member& m (*s.member);

        os << "// " << m.name () << endl
           << "//" << endl;

        // If the section is soft- added or deleted, check the version.
        //
        unsigned long long av (added (m));
        unsigned long long dv (deleted (m));
        if (av != 0 || dv != 0)
        {
          os << "if (";

          if (av != 0)
            os << "svm >= schema_version_migration (" << av << "ULL, true)";

          if (av != 0 && dv != 0)
            os << " &&" << endl;

          if (dv != 0)
            os << "svm <= schema_version_migration (" << dv << "ULL, true)";

          os << ")";
        }

        os << "{";

        // Section access is always by reference.
        //
        member_access& ma (m.get<member_access> ("get"));
        if (!ma.synthesized)
          os << "// From " << location_string (ma.loc, true) << endl;

        // VC++ cannot grok the constructor syntax.
        //
        os << "const odb::section& s = " << ma.translate ("obj") << ";"
           << endl;

        os << "if (";

        // Unless we are always loaded, test for being loaded.
        //
        if (s.load != user_section::load_eager)
          os << "s.loaded () && ";

        // And for not already being changed.
        //
        os << "!s.changed ())"
           << "{";

        instance<container_calls> t (container_calls::section_call, &s);
        t->traverse (c);

        os << "}" // if
           << "}";
      }

      if (poly_derived)
      {
        bool readonly_base (context::readonly (*poly_base));

        if (!sts && (readonly_base ||
                     update_columns ||
                     update_containers ||
                     sections))
        {
          os << db << "::transaction& tr (" << db <<
            "::transaction::current ());"
             << db << "::connection& conn (tr.connection ());"
             << "statements_type& sts (" << endl
             << "conn.statement_cache ().find_object<object_type> ());"
             << endl;
        }

        // Unless our base is readonly, call it first.
        //
        if (!readonly_base)
        {
          os << "base_traits::update (db, obj, false, false);"
             << endl;
        }
        else
        {
          // Otherwise, we have to initialize the id image ourselves. If
          // we don't have any columns, containers or sections to update,
          // then we only have to do it if this is not a top-level call.
          // If we are abstract, then top is always false.
          //
          if (!update_columns && !update_containers && !sections && !abst)
            os << "if (!top)";

          os << "{"
             << "id_image_type& i (sts.id_image ());";

          if (!id_ma->synthesized)
            os << "// From " << location_string (id_ma->loc, true) << endl;

          os << "init (i, " << id_ma->translate ("obj") << ");"
             << endl;

          os << "binding& idb (sts.id_image_binding ());"
             << "if (i.version != sts.id_image_version () || idb.version == 0)"
             << "{"
             << "bind (idb.bind, i);"
             << "sts.id_image_version (i.version);"
             << "idb.version++;"
            // Optimistic poly base cannot be readonly.
             << "}"
             << "}";
        }

        if (update_columns)
        {
          // Initialize the object image.
          //
          os << "image_type& im (sts.image ());";

          if (generate_grow)
            os << "if (";

          os << "init (im, obj, statement_update" <<
            (versioned ? ", svm" : "") << ")";

          if (generate_grow)
            os << ")" << endl
               << "im.version++";

          os << ";"
             << endl;

          os << "const binding& idb (sts.id_image_binding ());"
             << "binding& imb (sts.update_image_binding ());"
             << "if (idb.version != sts.update_id_binding_version () ||" << endl
             << "im.version != sts.update_image_version () ||" << endl
             << "imb.version == 0)"
             << "{"
             << "bind (imb.bind, idb.bind, idb.count, im, statement_update" <<
            (versioned ? ", svm" : "") << ");"
             << "sts.update_id_binding_version (idb.version);"
             << "sts.update_image_version (im.version);"
             << "imb.version++;"
             << "}";

          os << "update_statement& st (sts.update_statement ());"
             << "if (";

          if (versioned)
            os << "!st.empty () && ";

          os << "st.execute () == 0)" << endl
             << "throw object_not_persistent ();"
             << endl;
        }

        // Otherwise, nothing else to do here if we don't have any columns
        // to update.
      }
      else if (update_columns)
      {
        if (!sts)
          os << db << "::transaction& tr (" << db <<
            "::transaction::current ());"
             << db << "::connection& conn (tr.connection ());"
             << "statements_type& sts (" << endl
             << "conn.statement_cache ().find_object<object_type> ());"
             << endl;

        // Initialize id image.
        //
        if (!id_ma->synthesized)
          os << "// From " << location_string (id_ma->loc, true) << endl;

        os << "const id_type& id (" << endl
           << id_ma->translate ("obj") << ");";

        if (opt != 0)
        {
          if (!opt_ma_get->synthesized)
            os << "// From " << location_string (opt_ma_get->loc, true) << endl;

          os << "const version_type& v (" << endl
             << opt_ma_get->translate ("obj") << ");";
        }

        os << "id_image_type& idi (sts.id_image ());"
           << "init (idi, id";

        if (opt != 0)
          os << ", &v";

        os << ");"
           << endl;

        // Initialize object image.
        //
        os << "image_type& im (sts.image ());";

        if (generate_grow)
          os << "if (";

        os << "init (im, obj, statement_update" <<
          (versioned ? ", svm" : "") << ")";

        if (generate_grow)
          os << ")" << endl
             << "im.version++";

        os << ";"
           << endl;

        // The update binding is bound to two images (object and id)
        // so we have to track both versions.
        //
        os << "bool u (false);" // Avoid incrementing version twice.
           << "binding& imb (sts.update_image_binding ());"
           << "if (im.version != sts.update_image_version () ||" << endl
           << "imb.version == 0)"
           << "{"
           << "bind (imb.bind, im, statement_update" <<
          (versioned ? ", svm" : "") << ");"
           << "sts.update_image_version (im.version);"
           << "imb.version++;"
           << "u = true;"
           << "}";

        // To update the id part of the update binding we have to do
        // it indirectly via the id binding, which just points to the
        // suffix of the update bind array (see object_statements).
        //
        os << "binding& idb (sts.id_image_binding ());"
           << "if (idi.version != sts.update_id_image_version () ||" << endl
           << "idb.version == 0)"
           << "{"
          // If the id binding is up-to-date, then that means update
          // binding is too and we just need to update the versions.
          //
           << "if (idi.version != sts.id_image_version () ||" << endl
           << "idb.version == 0)"
           << "{"
           << "bind (idb.bind, idi);"
          // Update the id binding versions since we may use them later
          // to update containers.
          //
           << "sts.id_image_version (idi.version);"
           << "idb.version++;";
        if (opt != 0)
          os << "sts.optimistic_id_image_binding ().version++;";
        os << "}"
           << "sts.update_id_image_version (idi.version);"
           << endl
           << "if (!u)" << endl
           << "imb.version++;"
           << "}";

        os << "update_statement& st (sts.update_statement ());"
           << "if (";

        if (versioned)
          os << "!st.empty () && ";

        os << "st.execute () == 0)" << endl;

        if (opt == 0)
          os << "throw object_not_persistent ();";
        else
          os << "throw object_changed ();";

        os << endl;
      }
      else if (poly || update_containers || sections)
      {
        // We don't have any columns to update but we may still need
        // to initialize the id image if we are polymorphic or have
        // any containers or sections.
        //
        if (!sts)
          os << db << "::transaction& tr (" << db <<
            "::transaction::current ());"
             << db << "::connection& conn (tr.connection ());"
             << "statements_type& sts (" << endl
             << "conn.statement_cache ().find_object<object_type> ());"
             << endl;

        // The same rationale as a couple of screens up.
        //
        if (poly && !update_containers && !sections && !abst)
          os << "if (!top)";

        os << "{"
           << "id_image_type& i (sts.id_image ());";

        if (!id_ma->synthesized)
          os << "// From " << location_string (id_ma->loc, true) << endl;

        os << "init (i, " << id_ma->translate ("obj") << ");"
           << endl;

        os << "binding& idb (sts.id_image_binding ());"
           << "if (i.version != sts.id_image_version () || idb.version == 0)"
           << "{"
           << "bind (idb.bind, i);"
           << "sts.id_image_version (i.version);"
           << "idb.version++;"
          // Cannot be optimistic.
           << "}"
           << "}";
      }

      if (update_containers || sections)
        os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
           << endl;

      if (update_containers)
      {
        instance<container_calls> t (container_calls::update_call,
                                     &main_section);
        t->traverse (c);
      }

      // Update the optimistic concurrency version in the object member.
      // Do it before updating sections since they will update it as well.
      // The same code as in section_traits.
      //
      if (opt != 0 && !poly_derived)
      {
        // Object is passed as const reference so we need to cast away
        // constness.
        //
        string obj ("const_cast< object_type& > (obj)");
        string inc (optimistic_version_increment (*opt));

        if (!opt_ma_set->synthesized)
          os << "// From " << location_string (opt_ma_set->loc, true) << endl;

        if (opt_ma_set->placeholder ())
        {
          if (!opt_ma_get->synthesized)
            os << "// From " << location_string (opt_ma_get->loc, true) <<
              endl;

          if (inc == "1")
            os << opt_ma_set->translate (
              obj, opt_ma_get->translate ("obj") + " + 1") << ";";
          else
            os << opt_ma_set->translate (obj, inc) << ";";

          os << endl;
        }
        else
        {
          // If this member is const and we have a synthesized direct access,
          // then cast away constness. Otherwise, we assume that the user-
          // provided expression handles this.
          //
          bool cast (opt_ma_set->direct () && const_type (opt->type ()));
          if (cast)
            os << "const_cast< version_type& > (" << endl;

          os << opt_ma_set->translate (obj);

          if (cast)
            os << ")";

          if (inc == "1")
            os << "++;";
          else
            os << " = " << inc << ";";

          os << endl;
        }
      }

      // Update sections that are loaded and need updating.
      //
      for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
      {
        if (i->update_empty () || i->update == user_section::update_manual)
          continue;

        // Here is the deal: for polymorphic section overrides, we could
        // have used the same approach as in load_() where the class that
        // defines the section data member initiates the section update.
        // However, if we used this approach, then we would get what can
        // be called "interleaved" update statements. That is, a section
        // update would update the section data in "derived" tables before
        // updating the main section in these derived tables. While this
        // does not feel right, it can also cause more real problems if,
        // for example, there are constraints or some such. Generally,
        // we always want to update the main section data first for each
        // table in the hierarchy.
        //
        // So what we are going to do instead is call section traits
        // update at each override point (and indicate to it not to
        // call base). One tricky aspect is who is going to reset the
        // changed state. We have to do it at the top since otherwise
        // overrides won't know the section has changed. Finding out
        // whether we are the final override (in section terms, not
        // object terms) is tricky.
        //
        data_member& m (*i->member);

        // If the section is soft- added or deleted, check the version.
        //
        unsigned long long av (added (m));
        unsigned long long dv (deleted (m));
        if (av != 0 || dv != 0)
        {
          os << "if (";

          if (av != 0)
            os << "svm >= schema_version_migration (" << av << "ULL, true)";

          if (av != 0 && dv != 0)
            os << " &&" << endl;

          if (dv != 0)
            os << "svm <= schema_version_migration (" << dv << "ULL, true)";

          os << ")"
             << "{";
        }

        // Section access is always by reference.
        //
        member_access& ma (m.get<member_access> ("get"));
        if (!ma.synthesized)
          os << "// From " << location_string (ma.loc, true) << endl;

        os << "if (";

        // Unless we are always loaded, test for being loaded.
        //
        string sep;
        if (i->load != user_section::load_eager)
        {
          os << ma.translate ("obj") << ".loaded ()";
          sep = " && ";
        }

        // If change-updated section, check that it has changed.
        //
        if (i->update == user_section::update_change)
          os << sep << ma.translate ("obj") << ".changed ()";

        os << ")"
           << "{";

        // Re-initialize the id+ver image with new version which may
        // have changed. Here we take a bit of a shortcut and not
        // re-bind the image since we know only version may have
        // changed and that is always an integer (cannot grow).
        //
        if (opt != 0)
        {
          if (!opt_ma_get->synthesized)
            os << "// From " << location_string (opt_ma_get->loc, true) << endl;

          os << "const version_type& v (" << endl
             << opt_ma_get->translate ("obj") << ");";

          if (!id_ma->synthesized)
          os << "// From " << location_string (id_ma->loc, true) << endl;

          os << "init (sts.id_image (), " << id_ma->translate ("obj") <<
            ", &v);"
             << endl;
        }

        os << public_name (m) << "_traits::update (esc, obj";

        if (poly_derived && i->base != 0)
        {
          // Normally we don't want to call base (base object's update()
          // would have called it; see comment above). There is one
          // exception, however, and that is a readonly base section
          // in optimistic class. In this case, base object's update()
          // won't call it (it is readonly) but it needs to be called
          // in order to increment the version.
          //
          bool base (false);
          for (user_section* b (i->base); !base && b != 0; b = b->base)
          {
            if (b->total != b->inverse + b->readonly ||
                b->readwrite_containers)
              break; // We have a readwrite base.
            else if (b->optimistic ())
              base = true; // We have a readonly optimistic root.
          }

          os << ", " << base;
        }

        os << ");";

        // Clear the change flag and arm the transaction rollback callback.
        //
        if (i->update == user_section::update_change)
        {
          // If polymorphic, do it only if we are the final override.
          //
          if (poly)
            os << "if (root_traits::map->find (typeid (obj))." <<
              "final_section_update (info, " << i->index << "UL))" << endl;

          os << ma.translate ("obj") << ".reset (true, false, &tr);";
        }

        os << "}";

        if (av != 0 || dv != 0)
          os << "}";
      }

      // Call callback (post_update).
      //
      if (!abst) // If we are poly-abstract, then top will always be false.
      {
        if (poly)
          os << "if (top)"
             << "{";

        os << "callback (db, obj, callback_event::post_update);"
           << "pointer_cache_traits::update (db, obj);";

        if (poly)
          os << "}";
      }
    } // readonly

    os << "}";
  }

  // update () bulk
  //
  if (id != 0 && !readonly && c.count ("bulk-update"))
  {
    os << "void " << traits << "::" << endl
       << "update (database& db," << endl
       << "const object_type** objs," << endl
       << "std::size_t n," << endl
       << "multiple_exceptions& mex)"
       << "{"
       << "ODB_POTENTIALLY_UNUSED (db);"
       << endl
       << "using namespace " << db << ";"
       << "using " << db << "::update_statement;"
       << endl
       << db << "::connection& conn (" << endl
       << db << "::transaction::current ().connection ());"
       << "statements_type& sts (" << endl
       << "conn.statement_cache ().find_object<object_type> ());";

    if (versioned)
      os << "const schema_version_migration& svm (" <<
        "sts.version_migration (" << schema_name << "));";

    os << endl
       << "for (std::size_t i (0); i != n; ++i)"
       << "{"
       << "const object_type& obj (*objs[i]);"
       << "callback (db, obj, callback_event::pre_update);";

    if (!id_ma->synthesized)
      os << "// From " << location_string (id_ma->loc, true) << endl;

    os << "const id_type& id (" << endl
       << id_ma->translate ("obj") << ");";

    if (opt != 0)
    {
      if (!opt_ma_get->synthesized)
        os << "// From " << location_string (opt_ma_get->loc, true) << endl;

      os << "const version_type& v (" << endl
         << opt_ma_get->translate ("obj") << ");";
    }

    os << "init (sts.id_image (i), id" << (opt != 0 ? ", &v" : "") << ");"
      //@@ assumption: generate_grow false
       << "init (sts.image (i), obj, statement_update);"
       << "}";

    // Update bindings.
    //
    os << "binding& idb (sts.id_image_binding ());"
       << "binding& imb (sts.update_image_binding ());"
       << endl;

    //@@ assumption: generate_grow false
    //
    os << "bool u (false);" // Avoid incrementing version twice.
       << "if (imb.version == 0)"
       << "{"
       << "bind (imb.bind, sts.image (), statement_update" <<
      (versioned ? ", svm" : "") << ");"
       << "imb.version++;"
       << "u = true;"
       << "}";

    //@@ assumption: generate_grow false
    //
    os << "if (idb.version == 0)"
       << "{"
       << "bind (idb.bind, sts.id_image ());"
       << "idb.version++;";
    if (opt != 0)
      os << "sts.optimistic_id_image_binding ().version++;";
    os << endl
       << "if (!u)" << endl
       << "imb.version++;"
       << "}";

    // We don't need the versioned check here since the statement cannot
    // be empty; otherwise it would have been an empty object which is
    // illegal.
    //
    os << "update_statement& st (sts.update_statement ());"
       << "n = st.execute (n, mex);"; // Actual number of rows attempted.

    os << endl
       << "for (std::size_t i (0); i != n; ++i)"
       << "{"
       << "unsigned long long r (st.result (i));" // Also sets current in mex.
       << endl
       << "if (mex[i] != 0)" << endl // Pending exception from result().
       << "continue;"
       << endl
       << "if (r != 1)"
       << "{"
       << "mex.insert (i," << endl
      //@@ assumption: result_unknown
       << "(r == update_statement::result_unknown)," << endl
       << (opt == 0 ? "object_not_persistent" : "object_changed") << " ());"
       << "continue;"
       << "}"
       << "if (mex.fatal ())" << endl // Don't do any extra work.
       << "continue;"
       << endl
       << "const object_type& obj (*objs[i]);";

    // Update the optimistic concurrency version in the object member.
    //
    if (opt != 0)
    {
      // Object is passed as const reference so we need to cast away
      // constness.
      //
      string obj ("const_cast< object_type& > (obj)");
      string inc (optimistic_version_increment (*opt, true));

      if (!opt_ma_set->synthesized)
        os << "// From " << location_string (opt_ma_set->loc, true) << endl;

      if (opt_ma_set->placeholder ())
      {
        if (!opt_ma_get->synthesized)
          os << "// From " << location_string (opt_ma_get->loc, true) <<
            endl;

        if (inc == "1")
          os << opt_ma_set->translate (
            obj, opt_ma_get->translate ("obj") + " + 1") << ";";
        else
          os << opt_ma_set->translate (obj, inc) << ";";

        os << endl;
      }
      else
      {
        // If this member is const and we have a synthesized direct access,
        // then cast away constness. Otherwise, we assume that the user-
        // provided expression handles this.
        //
        bool cast (opt_ma_set->direct () && const_type (opt->type ()));
        if (cast)
          os << "const_cast< version_type& > (" << endl;

        os << opt_ma_set->translate (obj);

        if (cast)
          os << ")";

        if (inc == "1")
          os << "++;";
        else
          os << " = " << inc << ";";

        os << endl;
      }
    }

    os << "callback (db, obj, callback_event::post_update);"
       << "pointer_cache_traits::update (db, obj);"
       << "}"  // for
       << "}"; // update()
  }

  // erase (id)
  //
  size_t erase_containers (has_a (c, test_straight_container));
  bool erase_versioned_containers (
    erase_containers >
    has_a (c, test_straight_container | exclude_deleted | exclude_added));

  if (id != 0)
  {
    os << "void " << traits << "::" << endl
       << "erase (database& db, const id_type& id";

    if (poly)
      os << ", bool top, bool dyn";

    os << ")"
       << "{"
       << "using namespace " << db << ";"
       << endl
       << "ODB_POTENTIALLY_UNUSED (db);";

    if (poly)
      os << "ODB_POTENTIALLY_UNUSED (top);";

    os << endl
       << db << "::connection& conn (" << endl
       << db << "::transaction::current ().connection ());"
       << "statements_type& sts (" << endl
       << "conn.statement_cache ().find_object<object_type> ());"
       << endl;

    // Get the discriminator and determine the dynamic type of
    // this object.
    //
    if (poly)
    {
      os << "if (dyn)" << endl
         << "{"
         << "discriminator_type d;"
         << "root_traits::discriminator_ (sts.root_statements (), id, &d);";

      if (!abst)
        os << endl
           << "if (d != info.discriminator)"
           << "{";

      os << "const info_type& pi (root_traits::map->find (d));"
         << endl
        // Check that the dynamic type is derived from the static type.
        //
         << "if (!pi.derived (info))" << endl
         << "throw object_not_persistent ();"
         << endl
         << "pi.dispatch (info_type::call_erase, db, 0, &id);"
         << "return;";

      if (!abst)
        os << "}";

      os << "}";
    }

    // Initialize id image.
    //
    if (!abst) // If we are poly-abstract, then top will always be false.
    {
      if (poly)
        os << "if (top)"
           << "{";

      os << "id_image_type& i (sts.id_image ());"
         << "init (i, id);"
         << endl;

      os << "binding& idb (sts.id_image_binding ());"
         << "if (i.version != sts.id_image_version () || idb.version == 0)"
         << "{"
         << "bind (idb.bind, i);"
         << "sts.id_image_version (i.version);"
         << "idb.version++;";
      if (opt != 0)
        os << "sts.optimistic_id_image_binding ().version++;";
      os << "}";

      if (poly)
        os << "}";
    }

    // Erase containers first so that there are no reference
    // violations (we don't want to rely on ON DELETE CASCADE
    // here since in case of a custom schema, it might not be
    // there).
    //
    if (erase_containers)
    {
      os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());";

      if (erase_versioned_containers)
        os << "const schema_version_migration& svm (" <<
          "sts.version_migration (" << schema_name << "));";

      os << endl;

      instance<container_calls> t (container_calls::erase_id_call);
      t->traverse (c);
    }

    os << "if (sts.erase_statement ().execute () != 1)" << endl
       << "throw object_not_persistent ();"
       << endl;

    if (poly_derived)
    {
      // Call our base last (we erase polymorphic objects from base
      // to derived in order not to trigger cascading deletes).
      //
      os << "base_traits::erase (db, id, false, false);"
         << endl;
    }

    // Remove from the object cache.
    //
    if (!abst) // If we are poly-abstract, then top will always be false.
    {
      if (poly)
        os << "if (top)" << endl;

      os << "pointer_cache_traits::erase (db, id);";
    }

    os << "}";
  }

  // erase (id) bulk
  //
  if (id != 0 && c.count ("bulk-erase"))
  {
    os << "std::size_t " << traits << "::" << endl
       << "erase (database& db," << endl
       << "const id_type** ids," << endl
       << "std::size_t n," << endl
       << "multiple_exceptions& mex)"
       << "{"
       << "using namespace " << db << ";"
       << endl
       << "ODB_POTENTIALLY_UNUSED (db);"
       << endl
       << db << "::connection& conn (" << endl
       << db << "::transaction::current ().connection ());"
       << "statements_type& sts (" << endl
       << "conn.statement_cache ().find_object<object_type> ());"
       << endl
       << "for (std::size_t i (0); i != n; ++i)" << endl
       << "init (sts.id_image (i), *ids[i]);"
       << endl
       << "binding& idb (sts.id_image_binding ());"
      //@@ assumption: generate_grow false
       << "if (idb.version == 0)"
       << "{"
       << "bind (idb.bind, sts.id_image ());"
       << "idb.version++;"
       << (opt != 0 ? "sts.optimistic_id_image_binding ().version++;" : "")
       << "}"
       << "delete_statement& st (sts.erase_statement ());"
       << "n = st.execute (n, mex);" // Set to actual number of rows attempted.
       << endl
       << "for (std::size_t i (0); i != n; ++i)"
       << "{"
       << "unsigned long long r (st.result (i));" // Sets current in mex.
       << endl
       << "if (mex[i] != 0)" << endl // Pending exception from result().
       << "continue;"
       << endl
       << "if (r != 1)"
       << "{"
       << "mex.insert (i," << endl
      //@@ assumption: result_unknown
       << "(r == delete_statement::result_unknown)," << endl
       << "object_not_persistent ());"
       << "continue;"
       << "}"
       << "if (mex.fatal ())" << endl // Don't do any extra work.
       << "continue;"
       << "pointer_cache_traits::erase (db, *ids[i]);"
       << "}"  // for
       << "return n;"
       << "}"; // erase()
  }

  // erase (object)
  //
  bool erase_smart_containers (has_a (c, test_smart_container));

  if (id != 0 && (poly || opt != 0 || erase_smart_containers))
  {
    os << "void " << traits << "::" << endl
       << "erase (database& db, const object_type& obj";

    if (poly)
      os << ", bool top, bool dyn";

    os << ")"
       << "{"
       << "ODB_POTENTIALLY_UNUSED (db);";

    if (poly)
      os << "ODB_POTENTIALLY_UNUSED (top);";

    os << endl;

    if (poly)
      os << "if (dyn)" << endl
         << "{"
         << "const std::type_info& t (typeid (obj));"
         << endl
         << "if (t != info.type)"
         << "{"
         << "const info_type& pi (root_traits::map->find (t));"
         << "pi.dispatch (info_type::call_erase, db, &obj, 0);"
         << "return;"
         << "}"
         << "}";

    // If we are database-poly-abstract but not C++-abstract, then make
    // sure we are not trying to erase an instance of an abstract class.
    //
    if (abst && !c.abstract ())
      os << "if (top)" << endl
         << "throw abstract_class ();"
         << endl;

    if (opt != 0 || erase_smart_containers)
    {
      string rsts (poly_derived ? "rsts" : "sts");

      os << "using namespace " << db << ";"
         << endl
         << db << "::connection& conn (" << endl
         << db << "::transaction::current ().connection ());"
         << "statements_type& sts (" << endl
         << "conn.statement_cache ().find_object<object_type> ());";

      if (poly_derived)
        os << endl
           << "root_statements_type& rsts (sts.root_statements ());"
           << "ODB_POTENTIALLY_UNUSED (rsts);";

      os << endl;

      // Call callback (pre_erase).
      //
      if (!abst) // If we are poly-abstract, then top will always be false.
      {
        if (poly)
          os << "if (top)" << endl;

        os << "callback (db, obj, callback_event::pre_erase);"
           << endl;
      }

      if (!abst || erase_containers)
      {
        if (!id_ma->synthesized)
          os << "// From " << location_string (id_ma->loc, true) << endl;

        os << "const id_type& id  (" << endl
           << id_ma->translate ("obj") << ");"
           << endl;
      }

      // Smart containers case.
      //
      if (opt == 0)
      {
        // Initialize id image.
        //
        if (!abst) // If we are poly-abstract, then top will always be false.
        {
          if (poly)
            os << "if (top)"
               << "{";

          os << "id_image_type& i (" << rsts << ".id_image ());"
             << "init (i, id);"
             << endl;

          os << "binding& idb (" << rsts << ".id_image_binding ());"
             << "if (i.version != " << rsts << ".id_image_version () || " <<
            "idb.version == 0)"
             << "{"
             << "bind (idb.bind, i);"
             << rsts << ".id_image_version (i.version);"
             << "idb.version++;"
            // Cannot be optimistic.
             << "}";

          if (poly)
            os << "}";
        }

        // Erase containers first so that there are no reference
        // violations (we don't want to rely on ON DELETE CASCADE
        // here since in case of a custom schema, it might not be
        // there).
        //

        if (erase_containers)
        {
          os << "extra_statement_cache_type& esc (" <<
            "sts.extra_statement_cache ());";

          if (erase_versioned_containers)
            os << "const schema_version_migration& svm (" <<
              "sts.version_migration (" << schema_name << "));";

          os << endl;

          instance<container_calls> t (container_calls::erase_obj_call);
          t->traverse (c);
        }

        os << "if (sts.erase_statement ().execute () != 1)" << endl
           << "throw object_not_persistent ();"
           << endl;
      }
      // Optimistic case.
      //
      else
      {
        // Initialize id + managed column image.
        //
        if (!abst) // If we are poly-abstract, then top will always be false.
        {
          if (poly)
            os << "if (top)"
               << "{";

          if (!opt_ma_get->synthesized)
            os << "// From " << location_string (opt_ma_get->loc, true) << endl;

          os << "const version_type& v (" << endl
             << opt_ma_get->translate ("obj") << ");"
             << "id_image_type& i (" << rsts << ".id_image ());"
             << "init (i, id, &v);"
             << endl;

          os << "binding& idb (" << rsts << ".id_image_binding ());"
             << "if (i.version != " << rsts << ".id_image_version () ||" << endl
             << "idb.version == 0)"
             << "{"
             << "bind (idb.bind, i);"
             << rsts << ".id_image_version (i.version);"
             << "idb.version++;"
             << rsts << ".optimistic_id_image_binding ().version++;"
             << "}";

          if (poly)
            os << "}"; // if (top)
        }

        // If this is a derived type in a polymorphic hierarchy, then we
        // need to check the version (stored in root) before we go ahead
        // and start deleting things. Also use the same code for root with
        // containers since it is more efficient than the find_() method
        // below.
        //
        bool svm (false);
        if (poly_derived || (poly && erase_containers))
        {
          // Only do the check in the top-level call.
          //
          if (!abst) // If we are poly-abstract, then top will always be false.
          {
            os << "if (top)"
               << "{"
               << "version_type v;"
               << "root_traits::discriminator_ (" << rsts << ", id, 0, &v);"
               << endl;

            if (!opt_ma_get->synthesized)
              os << "// From " << location_string (opt_ma_get->loc, true) << endl;

            os << "if (v != " << opt_ma_get->translate ("obj") << ")" << endl
               << "throw object_changed ();"
               << "}";
          }
        }
        else if (erase_containers)
        {
          // Things get complicated here: we don't want to trash the
          // containers and then find out that the versions don't match
          // and we therefore cannot delete the object. After all, there
          // is no guarantee that the user will abort the transaction.
          // In fact, a perfectly reasonable scenario is to reload the
          // object, re-check the condition, decide not to delete the
          // object, and then commit the transaction.
          //
          // There doesn't seem to be anything better than first making
          // sure we can delete the object, then deleting the container
          // data, and then deleting the object. To check that we can
          // delete the object we are going to use find_() and then
          // compare the versions. A special-purpose SELECT query would
          // have been more efficient but it would complicated and bloat
          // things significantly.
          //

          if (versioned)
          {
            os << "const schema_version_migration& svm (" <<
              "sts.version_migration (" << schema_name << "));"
               << endl;
            svm = true;
          }

          os << "if (!find_ (sts, &id" <<
            (versioned ? ", svm" : "") << "))" << endl
             << "throw object_changed ();"
             << endl;

          if (delay_freeing_statement_result)
            os << "sts.find_statement ().free_result ();"
               << endl;

          if (!opt_ma_get->synthesized)
            os << "// From " << location_string (opt_ma_get->loc, true) << endl;

          os << "if (version (sts.image ()) != " <<
            opt_ma_get->translate ("obj") << ")" << endl
             << "throw object_changed ();"
             << endl;
        }

        // Erase containers first so that there are no reference
        // violations (we don't want to rely on ON DELETE CASCADE
        // here since in case of a custom schema, it might not be
        // there).
        //
        if (erase_containers)
        {
          os << "extra_statement_cache_type& esc (" <<
            "sts.extra_statement_cache ());";

          if (erase_versioned_containers && !svm)
            os << "const schema_version_migration& svm (" <<
              "sts.version_migration (" << schema_name << "));";

          os << endl;

          instance<container_calls> t (container_calls::erase_obj_call);
          t->traverse (c);
        }

        const char* st (
          poly_derived ? "erase_statement" : "optimistic_erase_statement");

        os << "if (sts." << st << " ().execute () != 1)" << endl
           << "throw object_changed ();"
           << endl;
      }

      if (poly_derived)
      {
        // Call our base last (we erase polymorphic objects from base
        // to derived in order not to trigger cascading deletes).
        //
        os << "base_traits::erase (db, obj, false, false);"
           << endl;
      }

      if (!abst) // If we are poly-abstract, then top will always be false.
      {
        if (poly)
          os << "if (top)"
             << "{";

        // Note that we don't reset sections since the object is now
        // transient and the state of a section in a transient object
        // is undefined.

        // Remove from the object cache.
        //
        os << "pointer_cache_traits::erase (db, id);";

        // Call callback (post_erase).
        //
        os << "callback (db, obj, callback_event::post_erase);";

        if (poly)
          os << "}";
      }
    }
    else
    {
      os << "callback (db, obj, callback_event::pre_erase);"
         << "erase (db, id (obj), true, false);"
         << "callback (db, obj, callback_event::post_erase);";
    }

    os << "}";
  }

  // erase (object) bulk
  //
  if (id != 0 && c.count ("bulk-erase"))
  {
    os << "void " << traits << "::" << endl
       << "erase (database& db," << endl
       << "const object_type** objs," << endl
       << "std::size_t n," << endl
       << "multiple_exceptions& mex)"
       << "{";

    // In non-optimistic case delegate to erase(id).
    //
    if (opt == 0)
    {
      os << "id_type a[batch];"
         << "const id_type* p[batch];"
         << endl
         << "for (std::size_t i (0); i != n; ++i)"
         << "{"
         << "const object_type& obj (*objs[i]);"
         << "callback (db, obj, callback_event::pre_erase);"
         << "a[i] = id (obj);"
         << "p[i] = a + i;"
         << "}"
         << "n = erase (db, p, n, mex);"
         << endl
         << "if (mex.fatal ())" << endl // Don't do any extra work.
         << "return;"
         << endl
         << "for (std::size_t i (0); i != n; ++i)"
         << "{"
         << "if (mex[i] == 0)" << endl // No pending exception.
         << "callback (db, *objs[i], callback_event::post_erase);"
         << "}"; // for
    }
    else
    {
      os << "using namespace " << db << ";"
         << endl
         << "ODB_POTENTIALLY_UNUSED (db);"
         << endl
         << db << "::connection& conn (" << endl
         << db << "::transaction::current ().connection ());"
         << "statements_type& sts (" << endl
         << "conn.statement_cache ().find_object<object_type> ());"
         << endl
         << "for (std::size_t i (0); i != n; ++i)"
         << "{"
         << "const object_type& obj (*objs[i]);"
         << "callback (db, obj, callback_event::pre_erase);";

      if (!id_ma->synthesized)
        os << "// From " << location_string (id_ma->loc, true) << endl;

      os << "const id_type& id (" << endl
         << id_ma->translate ("obj") << ");";

      if (!opt_ma_get->synthesized)
        os << "// From " << location_string (opt_ma_get->loc, true) << endl;

      os << "const version_type& v (" << endl
         << opt_ma_get->translate ("obj") << ");";

      os << "init (sts.id_image (i), id, &v);"
         << "}";

      os << "binding& idb (sts.id_image_binding ());"
        //@@ assumption: generate_grow false
         << "if (idb.version == 0)"
         << "{"
         << "bind (idb.bind, sts.id_image ());"
         << "idb.version++;"
         << "sts.optimistic_id_image_binding ().version++;"
         << "}"
         << "delete_statement& st (sts.optimistic_erase_statement ());"
         << "n = st.execute (n, mex);" // Set to actual number of attempted.
         << endl
         << "for (std::size_t i (0); i != n; ++i)"
         << "{"
         << "unsigned long long r (st.result (i));" // Sets current in mex.
         << endl
         << "if (mex[i] != 0)" << endl // Pending exception from result().
         << "continue;"
         << endl
         << "if (r != 1)"
         << "{"
         << "mex.insert (i," << endl
        //@@ assumption: result_unknown
         << "(r == delete_statement::result_unknown)," << endl
         << "object_changed ());"
         << "continue;"
         << "}"
         << "if (mex.fatal ())" << endl // Don't do any extra work.
         << "continue;"
         << endl
         << "const object_type& obj (*objs[i]);";

      if (!id_ma->synthesized)
        os << "// From " << location_string (id_ma->loc, true) << endl;

      os << "const id_type& id (" << endl
         << id_ma->translate ("obj") << ");"
         << "pointer_cache_traits::erase (db, id);"
         << "callback (db, obj, callback_event::post_erase);"
         << "}"; // for
    }

    os << "}"; // erase()
  }

  // find (id)
  //
  if (id != 0 && c.default_ctor ())
  {
    string rsts (poly_derived ? "rsts" : "sts");

    os << traits << "::pointer_type" << endl
       << traits << "::" << endl
       << "find (database& db, const id_type& id)"
       << "{"
       << "using namespace " << db << ";"
       << endl;

    // First check the session.
    //
    os << "{";

    if (poly_derived)
      os << "root_traits::pointer_type rp (pointer_cache_traits::find (" <<
        "db, id));"
         << endl
         << "if (!root_traits::pointer_traits::null_ptr (rp))" << endl
         << "return" << endl
         << "  root_traits::pointer_traits::dynamic_pointer_cast<" <<
        "object_type> (rp);";
    else
      os << "pointer_type p (pointer_cache_traits::find (db, id));"
         << endl
         << "if (!pointer_traits::null_ptr (p))" << endl
         << "return p;";

    os << "}";

    os << db << "::connection& conn (" << endl
       << db << "::transaction::current ().connection ());"
       << "statements_type& sts (" << endl
       << "conn.statement_cache ().find_object<object_type> ());";

    if (versioned)
      os << "const schema_version_migration& svm (" <<
        "sts.version_migration (" << schema_name << "));";

    if (poly_derived)
      os << "root_statements_type& rsts (sts.root_statements ());";

    os << endl
       << "statements_type::auto_lock l (" << rsts << ");";

    if (delay_freeing_statement_result)
      os << "auto_result ar;";

    if (poly)
      os << "root_traits::discriminator_type d;";

    os << endl
       << "if (l.locked ())"
       << "{"
       << "if (!find_ (sts, &id" << (versioned ? ", svm" : "") << "))" << endl
       << "return pointer_type ();";

    if (delay_freeing_statement_result)
      os << endl
         << "ar.set (sts.find_statement (" << (poly_derived ? "depth" : "") <<
        "));";

    if (poly)
      os << "d = root_traits::discriminator (" << rsts << ".image ());";

    os << "}";

    if (poly)
    {
      // If statements are locked, then get the discriminator by
      // executing a special SELECT statement. We need it to be
      // able to create an object of the correct dynamic type
      // which will be loaded later.
      //
      os << "else" << endl
         << "root_traits::discriminator_ (" << rsts << ", id, &d);"
         << endl;

      if (abst)
        os << "const info_type& pi (root_traits::map->find (d));"
           << endl;
      else
        os << "const info_type& pi (" << endl
           << "d == info.discriminator ? info : root_traits::map->find (d));"
           << endl;
    }

    // Create the object.
    //
    if (poly_derived)
    {
      os << "root_traits::pointer_type rp (pi.create ());"
         << "pointer_type p (" << endl
         << "root_traits::pointer_traits::static_pointer_cast<object_type> " <<
        "(rp));"
         << "pointer_traits::guard pg (p);"
         << endl;

      // Insert it as a root pointer (for non-unique pointers, rp should
      // still be valid and for unique pointers this is a no-op).
      //
      os << "pointer_cache_traits::insert_guard ig (" << endl
         << "pointer_cache_traits::insert (db, id, rp));"
         << endl;
    }
    else
    {
      if (poly)
        os << "pointer_type p (pi.create ());";
      else
        os << "pointer_type p (" << endl
           << "access::object_factory<object_type, pointer_type>::create ());";

      os << "pointer_traits::guard pg (p);"
         << endl;

      os << "pointer_cache_traits::insert_guard ig (" << endl
         << "pointer_cache_traits::insert (db, id, p));"
         << endl;
    }

    os << "object_type& obj (pointer_traits::get_ref (p));"
       << endl
       << "if (l.locked ())"
       << "{"
       << "select_statement& st (sts.find_statement (" <<
      (poly_derived ? "depth" : "") << "));"
       << "ODB_POTENTIALLY_UNUSED (st);"
       << endl;

    if (poly)
      os << "callback_event ce (callback_event::pre_load);"
         << "pi.dispatch (info_type::call_callback, db, &obj, &ce);";
    else
      os << "callback (db, obj, callback_event::pre_load);";

    os << "init (obj, sts.image (), &db" << (versioned ? ", svm" : "") << ");";

    init_value_extra ();

    if (delay_freeing_statement_result)
      os << "ar.free ();";

    os << "load_ (sts, obj, false" << (versioned ? ", svm" : "") << ");";

    if (poly)
      // Load the dynamic part of the object unless static and dynamic
      // types are the same.
      //
      os << endl
         << "if (&pi != &info)"
         << "{"
         << "std::size_t d (depth);"
         << "pi.dispatch (info_type::call_load, db, &obj, &d);"
         << "}";

    os << rsts << ".load_delayed (" << (versioned ? "&svm" : "0") << ");"
       << "l.unlock ();";

    if (poly)
      os << "ce = callback_event::post_load;"
         << "pi.dispatch (info_type::call_callback, db, &obj, &ce);";
    else
      os << "callback (db, obj, callback_event::post_load);";

    os << "pointer_cache_traits::load (ig.position ());"
       << "}"
       << "else" << endl
       << rsts << ".delay_load (id, obj, ig.position ()" <<
      (poly ? ", pi.delayed_loader" : "") << ");"
       << endl;

    os << "ig.release ();"
       << "pg.release ();"
       << "return p;"
       << "}";
  }

  // find (id, obj)
  //
  if (id != 0)
  {
    string rsts (poly_derived ? "rsts" : "sts");

    os << "bool " << traits << "::" << endl
       << "find (database& db, const id_type& id, object_type& obj";

    if (poly)
      os << ", bool dyn";

    os << ")"
       << "{";

    if (poly)
      os << "ODB_POTENTIALLY_UNUSED (dyn);"
         << endl;

    os << "using namespace " << db << ";"
       << endl;

    if (poly)
    {
      if (!abst)
      os << "if (dyn)" << endl
         << "{";

      os << "const std::type_info& t (typeid (obj));";

      if (!abst)
        os << endl
           << "if (t != info.type)"
           << "{";

      os << "const info_type& pi (root_traits::map->find (t));"
         << "return pi.dispatch (info_type::call_find, db, &obj, &id);";

      if (!abst)
        os << "}"
           << "}";
    }

    if (!abst)
    {
      os << db << "::connection& conn (" << endl
         << db << "::transaction::current ().connection ());"
         << "statements_type& sts (" << endl
         << "conn.statement_cache ().find_object<object_type> ());";

      if (versioned)
        os << "const schema_version_migration& svm (" <<
          "sts.version_migration (" << schema_name << "));";

      if (poly_derived)
        os << "root_statements_type& rsts (sts.root_statements ());";

      // This can only be top-level call so auto_lock must succeed.
      //
      os << endl
         << "statements_type::auto_lock l (" << rsts << ");"
         << endl;

      os << "if (!find_ (sts, &id" <<
        (versioned ? ", svm" : "") << "))" << endl
         << "return false;"
         << endl;

      os << "select_statement& st (sts.find_statement (" <<
        (poly_derived ? "depth" : "") << "));"
         << "ODB_POTENTIALLY_UNUSED (st);"
         << endl;

      if (delay_freeing_statement_result)
        os << "auto_result ar (st);";

      os << "reference_cache_traits::position_type pos (" << endl
         << "reference_cache_traits::insert (db, id, obj));"
         << "reference_cache_traits::insert_guard ig (pos);"
         << endl
         << "callback (db, obj, callback_event::pre_load);"
         << "init (obj, sts.image (), &db" <<
        (versioned ? ", svm" : "") << ");";

      init_value_extra ();

      if (delay_freeing_statement_result)
        os << "ar.free ();";

      os << "load_ (sts, obj, false" << (versioned ? ", svm" : "") << ");"
         << rsts << ".load_delayed (" << (versioned ? "&svm" : "0") << ");"
         << "l.unlock ();"
         << "callback (db, obj, callback_event::post_load);"
         << "reference_cache_traits::load (pos);"
         << "ig.release ();"
         << "return true;";
    }

    os << "}";
  }

  // reload ()
  //
  if (id != 0)
  {
    string rsts (poly_derived ? "rsts" : "sts");

    // This implementation is almost exactly the same as find(id, obj)
    // above except that it doesn't interract with the object cache and,
    // in case of optimistic concurrency, checks if the object actually
    // needs to be reloaded.
    //
    os << "bool " << traits << "::" << endl
       << "reload (database& db, object_type& obj";

    if (poly)
      os << ", bool dyn";

    os << ")"
       << "{";

    if (poly)
      os << "ODB_POTENTIALLY_UNUSED (dyn);"
         << endl;

    os << "using namespace " << db << ";"
       << endl;

    if (poly)
    {
      if (!abst)
      os << "if (dyn)" << endl
         << "{";

      os << "const std::type_info& t (typeid (obj));";

      if (!abst)
        os << endl
           << "if (t != info.type)"
           << "{";

      os << "const info_type& pi (root_traits::map->find (t));"
         << "return pi.dispatch (info_type::call_reload, db, &obj, 0);";

      if (!abst)
        os << "}"
           << "}";
    }

    if (!abst)
    {
      os << db << "::connection& conn (" << endl
         << db << "::transaction::current ().connection ());"
         << "statements_type& sts (" << endl
         << "conn.statement_cache ().find_object<object_type> ());";

      if (versioned)
        os << "const schema_version_migration& svm (" <<
          "sts.version_migration (" << schema_name << "));";

      if (poly_derived)
        os << "root_statements_type& rsts (sts.root_statements ());";

      // This can only be top-level call so auto_lock must succeed.
      //
      os << endl
         << "statements_type::auto_lock l (" << rsts << ");"
         << endl;

      if (!id_ma->synthesized)
        os << "// From " << location_string (id_ma->loc, true) << endl;

      os << "const id_type& id  (" << endl
         << id_ma->translate ("obj") << ");"
         << endl;

      os << "if (!find_ (sts, &id" <<
        (versioned ? ", svm" : "") << "))" << endl
         << "return false;"
         << endl;

      os << "select_statement& st (sts.find_statement (" <<
        (poly_derived ? "depth" : "") << "));"
         << "ODB_POTENTIALLY_UNUSED (st);"
         << endl;

      if (delay_freeing_statement_result)
        os << "auto_result ar (st);"
           << endl;

      if (opt != 0)
      {
        if (!opt_ma_get->synthesized)
          os << "// From " << location_string (opt_ma_get->loc, true) << endl;

        os << "if (" << (poly_derived ? "root_traits::" : "") << "version (" <<
          rsts << ".image ()) == " << opt_ma_get->translate ("obj") <<
          ")" << endl
           << "return true;"
           << endl;
      }

      os << "callback (db, obj, callback_event::pre_load);"
         << "init (obj, sts.image (), &db" <<
        (versioned ? ", svm" : "") << ");";

      init_value_extra ();

      if (delay_freeing_statement_result)
        os << "ar.free ();";

      os << "load_ (sts, obj, true" << (versioned ? ", svm" : "") << ");"
         << rsts << ".load_delayed (" << (versioned ? "&svm" : "0") << ");"
         << "l.unlock ();"
         << "callback (db, obj, callback_event::post_load);"
         << "return true;";
    }

    os << "}";
  }

  // load (section) [non-thunk version]
  //
  if (uss.count (user_sections::count_new   |
                 user_sections::count_load  |
                 (poly ? user_sections::count_load_empty : 0)) != 0)
  {
    os << "bool " << traits << "::" << endl
       << "load (connection& conn, object_type& obj, section& s" <<
      (poly ? ", const info_type* pi" : "") << ")"
       << "{"
       << "using namespace " << db << ";"
       << endl;

    if (poly)
    {
      // Resolve type information if we are doing indirect calls.
      //
      os << "if (pi == 0)" // Top-level call.
         << "{"
         << "const std::type_info& t (typeid (obj));";

      if (!abst)
        os << endl
           << "if (t == info.type)" << endl
           << "pi = &info;"
           << "else" << endl;

      os << "pi = &root_traits::map->find (t);"
         << "}";
    }

    // If our poly-base has load sections, then call the base version
    // first. Besides checking for sections, it will also initialize
    // the id image.
    //
    if (poly_derived &&
        buss->count (user_sections::count_total |
                     user_sections::count_load  |
                     user_sections::count_load_empty) != 0)
    {
      os << "if (base_traits::load (conn, obj, s, pi))" << endl
         << "return true;"
         << endl;
    }
    else
    {
      // Resolve extra statements if we are doing direct calls.
      //
      os << db << "::connection& c (static_cast<" << db <<
        "::connection&> (conn));"
         << "statements_type& sts (c.statement_cache ()." <<
        "find_object<object_type> ());";

      if (!poly)
        os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());";

      os << endl;

      // Initialize id image. This is not necessarily the root of the
      // polymorphic hierarchy.
      //
      os << "id_image_type& i (sts.id_image ());";

      if (!id_ma->synthesized)
        os << "// From " << location_string (id_ma->loc, true) << endl;

      os << "init (i, " <<  id_ma->translate ("obj") << ");"
         << endl;

      os << "binding& idb (sts.id_image_binding ());"
         << "if (i.version != sts.id_image_version () || idb.version == 0)"
         << "{"
         << "bind (idb.bind, i);"
         << "sts.id_image_version (i.version);"
         << "idb.version++;";
      if (opt != 0)
        os << "sts.optimistic_id_image_binding ().version++;";
      os << "}";
    }

    // Dispatch.
    //
    bool e (false);
    for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
    {
      // Skip special sections.
      //
      if (i->special == user_section::special_version)
        continue;

      if (i->load == user_section::load_eager)
        continue;

      // Overridden sections are handled by the base.
      //
      if (poly_derived && i->base != 0)
        continue;

      data_member& m (*i->member);

      // Section access is always by reference.
      //
      member_access& ma (m.get<member_access> ("get"));
      if (!ma.synthesized)
        os << "// From " << location_string (ma.loc, true) << endl;

      if (e)
        os << "else ";
      else
        e = true;

      os << "if (&s == &" << ma.translate ("obj") << ")" << endl;

      if (!poly)
        os << public_name (m) << "_traits::load (esc, obj);";
      else
      {
        // If this is an empty section, then there may not be any
        // overrides.
        //
        os << "{"
           << "info_type::section_load sl (" <<
          "pi->find_section_load (" << i->index << "UL));";

        if (i->load_empty ())
          os << "if (sl != 0)" << endl;

        os << "sl (conn, obj, true);"
           << "}";
      }
    }

    os << "else" << endl
       << "return false;"
       << endl
       << "return true;"
       << "}";
  }

  // update (section) [non-thunk version]
  //
  if (uss.count (user_sections::count_new   |
                 user_sections::count_update  |
                 (poly ? user_sections::count_update_empty : 0)) != 0)
  {
    os << "bool " << traits << "::" << endl
       << "update (connection& conn, const object_type& obj, " <<
      "const section& s" << (poly ? ", const info_type* pi" : "") << ")"
       << "{"
       << "using namespace " << db << ";"
       << endl;

    if (poly)
    {
      // Resolve type information if we are doing indirect calls.
      //
      os << "if (pi == 0)" // Top-level call.
         << "{"
         << "const std::type_info& t (typeid (obj));";

      if (!abst)
        os << endl
           << "if (t == info.type)" << endl
           << "pi = &info;"
           << "else" << endl;

      os << "pi = &root_traits::map->find (t);"
         << "}";
    }

    // If our poly-base has update sections, then call the base version
    // first. Besides checking for sections, it will also initialize the
    // id image.
    //
    if (poly_derived &&
        buss->count (user_sections::count_total  |
                     user_sections::count_update |
                     user_sections::count_update_empty) != 0)
    {
      os << "if (base_traits::update (conn, obj, s, pi))" << endl
         << "return true;"
         << endl;
    }
    else
    {
      // Resolve extra statements if we are doing direct calls.
      //
      os << db << "::connection& c (static_cast<" << db <<
        "::connection&> (conn));"
         << "statements_type& sts (c.statement_cache ()." <<
        "find_object<object_type> ());";

      if (!poly)
        os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());";

      os << endl;

      // Initialize id image. This is not necessarily the root of the
      // polymorphic hierarchy.
      //
      if (opt != 0)
      {
        if (!opt_ma_get->synthesized)
          os << "// From " << location_string (opt_ma_get->loc, true) << endl;

        os << "const version_type& v (" << endl
           << opt_ma_get->translate ("obj") << ");";
      }

      os << "id_image_type& i (sts.id_image ());";

      if (!id_ma->synthesized)
        os << "// From " << location_string (id_ma->loc, true) << endl;

      os << "init (i, " <<  id_ma->translate ("obj") <<
        (opt != 0 ? ", &v" : "") << ");"
         << endl;

      os << "binding& idb (sts.id_image_binding ());"
         << "if (i.version != sts.id_image_version () || idb.version == 0)"
         << "{"
         << "bind (idb.bind, i);"
         << "sts.id_image_version (i.version);"
         << "idb.version++;";
      if (opt != 0)
        os << "sts.optimistic_id_image_binding ().version++;";
      os << "}";
    }

    // Dispatch.
    //
    bool e (false);
    for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
    {
      // Skip special sections.
      //
      if (i->special == user_section::special_version)
        continue;

      // Overridden sections are handled by the base.
      //
      if (poly_derived && i->base != 0)
        continue;

      // Skip read-only sections. Polymorphic sections are always
      // (potentially) read-write.
      //
      if (!poly && i->update_empty ())
        continue;

      data_member& m (*i->member);

      // Section access is always by reference.
      //
      member_access& ma (m.get<member_access> ("get"));
      if (!ma.synthesized)
        os << "// From " << location_string (ma.loc, true) << endl;

      if (e)
        os << "else ";
      else
        e = true;

      os << "if (&s == &" << ma.translate ("obj") << ")";

      if (!poly)
        os << public_name (m) << "_traits::update (esc, obj);";
      else
      {
        // If this is a readonly section, then there may not be any
        // overrides.
        //
        os << "{"
           << "info_type::section_update su (" <<
          "pi->find_section_update (" << i->index << "UL));";

        if (i->update_empty ())
        {
          // For optimistic section, also check that we are not the
          // final override, since otherwise we will increment the
          // version without updating anything.
          //
          if (i->optimistic ())
            os << "if (su != 0 && su != info.sections->functions[" <<
              i->index << "UL].update)" << endl;
          else
            os << "if (su != 0)" << endl;
        }

        os << "su (conn, obj);"
           << "}";
      }
    }

    os << "else" << endl
       << "return false;"
       << endl
       << "return true;"
       << "}";
  }

  // find_ ()
  //
  if (id != 0)
  {
    os << "bool " << traits << "::" << endl
       << "find_ (statements_type& sts," << endl
       << "const id_type* id";

    if (versioned)
      os << "," << endl
         << "const schema_version_migration& svm";

    if (poly_derived && !abst)
      os << "," << endl
         << "std::size_t d";

    os << ")"
       << "{"
       << "using namespace " << db << ";"
       << endl;

    // Initialize id image.
    //
    if (poly_derived && !abst)
      os << "if (d == depth)"
         << "{";

    os << "id_image_type& i (sts.id_image ());"
       << "init (i, *id);"
       << endl;

    os << "binding& idb (sts.id_image_binding ());"
       << "if (i.version != sts.id_image_version () || idb.version == 0)"
       << "{"
       << "bind (idb.bind, i);"
       << "sts.id_image_version (i.version);"
       << "idb.version++;";
    if (opt != 0)
      os << "sts.optimistic_id_image_binding ().version++;";
    os << "}";

    if (poly_derived && !abst)
      os << "}";

    // Rebind data image.
    //
    os << "image_type& im (sts.image ());"
       << "binding& imb (sts.select_image_binding (" <<
      (poly_derived ? (abst ? "depth" : "d") : "") << "));"
       << endl;

    if (poly_derived)
    {
      os << "if (imb.version == 0 ||" << endl
         << "check_version (sts.select_image_versions (), im))"
         << "{"
         << "bind (imb.bind, 0, 0, im, statement_select" <<
        (versioned ? ", svm" : "") << ");"
         << "update_version (sts.select_image_versions ()," << endl
         << "im," << endl
         << "sts.select_image_bindings ());"
         << "}";
    }
    else
    {
      os << "if (im.version != sts.select_image_version () ||" << endl
         << "imb.version == 0)"
         << "{"
         << "bind (imb.bind, im, statement_select" <<
        (versioned ? ", svm" : "") << ");"
         << "sts.select_image_version (im.version);"
         << "imb.version++;"
         << "}";
    }

    os << "select_statement& st (sts.find_statement (" <<
      (poly_derived ? (abst ? "depth" : "d") : "") << "));"
       << endl;

    // The dynamic loader SELECT statement can be dynamically empty.
    //
    if (versioned && poly_derived && !abst)
      os << "if (st.empty ())" << endl
         << "return true;"
         << endl;

    os << "st.execute ();"
       << "auto_result ar (st);"
       << "select_statement::result r (st.fetch ());"
       << endl;

    if (grow)
    {
      os << "if (r == select_statement::truncated)"
         << "{"
         << "if (grow (im, sts.select_image_truncated ()" <<
        (versioned ? ", svm" : "") <<
        (poly_derived ? (abst ? ", depth" : ", d") : "") << "))" << endl
         << "im.version++;"
         << endl;

      if (poly_derived)
      {
        os << "if (check_version (sts.select_image_versions (), im))"
           << "{"
           << "bind (imb.bind, 0, 0, im, statement_select" <<
          (versioned ? ", svm" : "") << ");"
           << "update_version (sts.select_image_versions ()," << endl
           << "im," << endl
           << "sts.select_image_bindings ());"
           << "st.refetch ();"
           << "}";
      }
      else
      {
        os << "if (im.version != sts.select_image_version ())"
           << "{"
           << "bind (imb.bind, im, statement_select" <<
          (versioned ? ", svm" : "") << ");"
           << "sts.select_image_version (im.version);"
           << "imb.version++;"
           << "st.refetch ();"
           << "}";
      }

      os << "}";
    }

    // If we are delaying, only free the result if it is empty.
    //
    if (delay_freeing_statement_result)
      os << "if (r != select_statement::no_data)"
         << "{"
         << "ar.release ();"
         << "return true;"
         << "}"
         << "else" << endl
         << "return false;";
    else
      os << "return r != select_statement::no_data;";

    os << "}";
  }

  // load_()
  //
  // Load containers, reset/reload sections.
  //
  size_t load_containers (
    has_a (c, test_container | include_eager_load, &main_section));

  if (poly_derived ||
      load_containers ||
      uss.count (user_sections::count_new  |
                 user_sections::count_load |
                 (poly ? user_sections::count_load_empty : 0)) != 0)
  {
    bool load_versioned_containers (
      load_containers >
      has_a (c,
             test_container | include_eager_load |
             exclude_deleted | exclude_added | exclude_versioned,
             &main_section));

    os << "void " << traits << "::" << endl
       << "load_ (statements_type& sts," << endl
       << "object_type& obj," << endl
       << "bool reload";

    if (versioned)
      os << "," << endl
         << "const schema_version_migration& svm";

    if (poly_derived)
      os << "," << endl
         << "std::size_t d";

    os << ")"
       << "{"
       << "ODB_POTENTIALLY_UNUSED (reload);";
    if (versioned)
      os << "ODB_POTENTIALLY_UNUSED (svm);";
    os << endl;

    if (poly_derived)
      os << "if (--d != 0)" << endl
         << "base_traits::load_ (sts.base_statements (), obj, reload" <<
        (context::versioned (*poly_base) ? ", svm" : "") <<
        (poly_base != poly_root ? ", d" : "") << ");"
         << endl;

    if (load_containers ||
        (!poly && uss.count (user_sections::count_new |
                             user_sections::count_load) != 0))
      os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
         << endl;

    if (!versioned && (
          load_versioned_containers ||
          uss.count (user_sections::count_new |
                     user_sections::count_all |
                     user_sections::count_versioned_only) != 0))
    {
      os << "const schema_version_migration& svm (" <<
        "sts.version_migration (" << schema_name << "));"
         << endl;
    }

    if (load_containers)
    {
      instance<container_calls> t (container_calls::load_call, &main_section);
      t->traverse (c);
    }

    for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
    {
      // Skip special sections.
      //
      if (i->special == user_section::special_version)
        continue;

      // Skip overridden sections; they are handled by the base.
      //
      if (i->base != 0 && poly_derived)
        continue;

      data_member& m (*i->member);

      // If the section is soft- added or deleted, check the version.
      //
      unsigned long long av (added (m));
      unsigned long long dv (deleted (m));
      if (av != 0 || dv != 0)
      {
        os << "if (";

        if (av != 0)
          os << "svm >= schema_version_migration (" << av << "ULL, true)";

        if (av != 0 && dv != 0)
          os << " &&" << endl;

        if (dv != 0)
          os << "svm <= schema_version_migration (" << dv << "ULL, true)";

        os << ")"
           << "{";
      }

      // Section access is always by reference.
      //
      member_access& ma (m.get<member_access> ("get"));
      if (!ma.synthesized)
        os << "// From " << location_string (ma.loc, true) << endl;

      if (i->load == user_section::load_eager)
      {
        // Mark an eager section as loaded.
        //
        os << ma.translate ("obj") << ".reset (true, false);"
           << endl;
        continue;
      }

      os << "if (reload)"
         << "{"
        // Reload sections that have been loaded, clear change state.
        //
         << "if (" << ma.translate ("obj") << ".loaded ())"
         << "{";

      if (!poly)
        os << public_name (m) << "_traits::load (esc, obj);";
      else
      {
        os << "info_type::section_load sl (" << endl
           << "root_traits::map->find (typeid (obj)).find_section_load (" <<
          i->index << "UL));";

        if (i->load_empty ())
          os << "if (sl != 0)" << endl;

        os << "sl (sts.connection (), obj, true);";
      }

      os << ma.translate ("obj") << ".reset (true, false);"
         << "}"
         << "}"
         << "else" << endl
        // Reset to unloaded, unchanged state.
         << ma.translate ("obj") << ".reset ();";

      if (av != 0 || dv != 0)
        os << "}";
      else
        os << endl;
    }

    os << "}";
  }

  // load_()
  //
  // Load the dynamic part of the object. We don't need it if we are
  // poly-abstract.
  //
  if (poly_derived && !abst)
  {
    os << "void " << traits << "::" << endl
       << "load_ (database& db, root_type& r, std::size_t d)"
       << "{"
       << "using namespace " << db << ";"
       << endl
       << "object_type& obj (static_cast<object_type&> (r));"
       << db << "::connection& conn (" << endl
       << db << "::transaction::current ().connection ());"
       << "statements_type& sts (" << endl
       << "conn.statement_cache ().find_object<object_type> ());"
       << endl
       << "d = depth - d;" // Convert to distance from derived.
       << endl;

    if (versioned)
      os << "const schema_version_migration& svm (" <<
        "sts.version_migration (" << schema_name << "));"
         << endl;

    // Avoid trying to execute an empty SELECT statement.
    //
    if (empty_depth != 0)
      os << "if (d > " << (poly_depth - empty_depth) << "UL)"
         << "{";

    os << "if (!find_ (sts, 0" << (versioned ? ", svm" : "") << ", d))" << endl
       << "throw object_not_persistent ();" // Database inconsistency.
       << endl;

    os << "select_statement& st (sts.find_statement (d));"
       << "ODB_POTENTIALLY_UNUSED (st);"
       << endl;

    // The resulting SELECT statement may end up being dynamically empty.
    //
    if (versioned)
      os << "if (!st.empty ())"
         << "{";

    if (delay_freeing_statement_result)
      os << "auto_result ar (st);";

    os << "init (obj, sts.image (), &db" << (versioned ? ", svm" : "") <<
      ", d);";

    init_value_extra ();

    if (delay_freeing_statement_result)
      os << "ar.free ();";

    if (versioned)
      os << "}";

    if (empty_depth != 0)
      os << "}";

    os << "load_ (sts, obj, false, " << (versioned ? "svm, " : "") << "d);"
       << "}";
  }

  // discriminator_ ()
  //
  if (poly && !poly_derived)
  {
    os << "void " << traits << "::" << endl
       << "discriminator_ (statements_type& sts," << endl
       << "const id_type& id," << endl
       << "discriminator_type* pd";

    if (opt != 0)
      os << "," << endl
         << "version_type* pv";

    os << ")"
       << "{"
       << "using namespace " << db << ";"
       << endl;

    // Initialize id image.
    //
    os << "id_image_type& idi (sts.discriminator_id_image ());"
       << "init (idi, id);"
       << endl;

    os << "binding& idb (sts.discriminator_id_image_binding ());"
       << "if (idi.version != sts.discriminator_id_image_version () ||" << endl
       << "idb.version == 0)"
       << "{"
       << "bind (idb.bind, idi" << (opt != 0 ? ", false" : "") << ");"
       << "sts.discriminator_id_image_version (idi.version);"
       << "idb.version++;"
       << "}";

    // Rebind data image.
    //
    os << "discriminator_image_type& i (sts.discriminator_image ());"
       << "binding& imb (sts.discriminator_image_binding ());"
       << endl
       << "if (i.version != sts.discriminator_image_version () ||" << endl
       << "imb.version == 0)"
       << "{"
      // Generate bind code inline. For now discriminator is simple
      // value so we don't need statement kind (sk).
      //
       << bind_vector << " b (imb.bind);"
       << "std::size_t n (0);"
       << "{";
    bind_discriminator_member_->traverse (*discriminator);
    os << "}";

    if (opt != 0)
    {
      os << "n++;" // For now discriminator is a simple value.
         << "{";
      bind_version_member_->traverse (*opt);
      os << "}";
    }

    os << "sts.discriminator_image_version (i.version);"
       << "imb.version++;"
       << "}";

    os << "{"
       << "select_statement& st (sts.find_discriminator_statement ());"
       << "st.execute ();"
       << "auto_result ar (st);"
       << "select_statement::result r (st.fetch ());"
       << endl
       << "if (r == select_statement::no_data)"
       << "{";

    if (opt != 0)
      os << "if (pv != 0)" << endl
         << "throw object_changed ();"
         << "else" << endl;

    os << "throw object_not_persistent ();"
       << "}";

    if (generate_grow &&
        (context::grow (*discriminator) ||
         (opt != 0 && context::grow (*opt))))
    {
      os << "else if (r == select_statement::truncated)"
         << "{";

      // Generate grow code inline.
      //
      os << "bool grew (false);"
         << truncated_vector << " t (sts.discriminator_image_truncated ());"
         << endl;

      index_ = 0;
      grow_discriminator_member_->traverse (*discriminator);

      if (opt != 0)
        grow_version_member_->traverse (*opt);

      os << "if (grew)" << endl
         << "i.version++;"
         << endl;

      os << "if (i.version != sts.discriminator_image_version ())"
         << "{"
        // Generate bind code inline. The same code as above.
        //
         << bind_vector << " b (imb.bind);"
         << "std::size_t n (0);"
         << "{";
      bind_discriminator_member_->traverse (*discriminator);
      os << "}";

      if (opt != 0)
      {
        os << "n++;" // For now discriminator is a simple value.
           << "{";
        bind_version_member_->traverse (*opt);
        os << "}";
      }

      os << "sts.discriminator_image_version (i.version);"
         << "imb.version++;"
         << "st.refetch ();"
         << "}"
         << "}";
    }

    // Discriminator cannot be long data (no streaming).
    //
    os << "}";

    // Initialize value inline instead of generating a separate
    // init() function. For now discriminator is simple value so
    // we don't need the database (db).
    //
    os << "if (pd != 0)"
       << "{"
       << "discriminator_type& d (*pd);";
    init_named_discriminator_value_member_->traverse (*discriminator);
    os << "}";

    if (opt != 0)
    {
      os << "if (pv != 0)"
         << "{"
         << "version_type& v (*pv);";
      init_named_version_value_member_->traverse (*opt);
      os << "}";
    }

    os << "}";
  }

  if (options.generate_query ())
  {
    char const* result_type;
    if (poly)
      result_type = "polymorphic_object_result_impl<object_type>";
    else if (id != 0)
      result_type = "object_result_impl<object_type>";
    else
      result_type = "no_id_object_result_impl<object_type>";

    string sep (versioned || query_optimize ? "\n" : " ");

    // Unprepared.
    //
    if (!options.omit_unprepared ())
    {
      // query ()
      //
      os << "result< " << traits << "::object_type >" << endl
         << traits << "::" << endl
         << "query (database&, const query_base_type& q)"
         << "{"
         << "using namespace " << db << ";"
         << "using odb::details::shared;"
         << "using odb::details::shared_ptr;"
         << endl;

      os << db << "::connection& conn (" << endl
         << db << "::transaction::current ().connection ());"
         << endl
         << "statements_type& sts (" << endl
         << "conn.statement_cache ().find_object<object_type> ());";

      if (versioned)
        os << "const schema_version_migration& svm (" <<
          "sts.version_migration (" << schema_name << "));";

      os << endl;

      // Rebind the image if necessary.
      //
      os << "image_type& im (sts.image ());"
         << "binding& imb (sts.select_image_binding (" <<
        (poly_derived ? "depth" : "") << "));"
         << endl;

      if (poly_derived)
      {
        os << "if (imb.version == 0 ||" << endl
           << "check_version (sts.select_image_versions (), im))"
           << "{"
           << "bind (imb.bind, 0, 0, im, statement_select" <<
          (versioned ? ", svm" : "") << ");"
           << "update_version (sts.select_image_versions ()," << endl
           << "im," << endl
           << "sts.select_image_bindings ());"
           << "}";
      }
      else
      {
        os << "if (im.version != sts.select_image_version () ||" << endl
           << "imb.version == 0)"
           << "{"
           << "bind (imb.bind, im, statement_select" <<
          (versioned ? ", svm" : "") << ");"
           << "sts.select_image_version (im.version);"
           << "imb.version++;"
           << "}";
      }

      os << "std::string text (query_statement);"
         << "if (!q.empty ())"
         << "{"
         << "text += " << strlit (sep) << ";"
         << "text += q.clause ();"
         << "}";

      os << "q.init_parameters ();"
         << "shared_ptr<select_statement> st (" << endl
         << "new (shared) select_statement (" << endl;
      object_query_statement_ctor_args (
        c, "q", versioned || query_optimize, false);
      os << "));" << endl
         << "st->execute ();";

      post_query_ (c, true);

      os << endl
         << "shared_ptr< odb::" << result_type << " > r (" << endl
         << "new (shared) " << db << "::" << result_type << " (" << endl
         << "q, st, sts, " << (versioned ? "&svm" : "0") << "));"
         << endl
         << "return result<object_type> (r);"
         << "}";

      // query(odb::query_base)
      //
      if (multi_dynamic)
        os << "result< " << traits << "::object_type >" << endl
           << traits << "::" << endl
           << "query (database& db, const odb::query_base& q)"
           << "{"
           << "return query (db, query_base_type (q));"
           << "}";
    }

    // erase_query
    //
    os << "unsigned long long " << traits << "::" << endl
       << "erase_query (database&, const query_base_type& q)"
       << "{"
       << "using namespace " << db << ";"
       << endl
       << db << "::connection& conn (" << endl
       << db << "::transaction::current ().connection ());"
       << endl
       << "std::string text (erase_query_statement);"
       << "if (!q.empty ())"
       << "{"
       << "text += ' ';"
       << "text += q.clause ();"
       << "}"
       << "q.init_parameters ();"
       << "delete_statement st (" << endl;
    object_erase_query_statement_ctor_args (c);
    os << ");"
       << endl
       << "return st.execute ();"
       << "}";

    // erase_query(odb::query_base)
    //
    if (multi_dynamic)
      os << "unsigned long long " << traits << "::" << endl
         << "erase_query (database& db, const odb::query_base& q)"
         << "{"
         << "return erase_query (db, query_base_type (q));"
         << "}";

    // Prepared. Very similar to unprepared but has some annoying variations
    // that make it difficult to factor out something common.
    //
    if (options.generate_prepared ())
    {
      // prepare_query
      //
      os << "odb::details::shared_ptr<prepared_query_impl>" << endl
         << traits << "::" << endl
         << "prepare_query (connection& c, const char* n, " <<
        "const query_base_type& q)"
         << "{"
         << "using namespace " << db << ";"
         << "using odb::details::shared;"
         << "using odb::details::shared_ptr;"
         << endl;

      os << db << "::connection& conn (" << endl
         << "static_cast<" << db << "::connection&> (c));"
         << endl
         << "statements_type& sts (" << endl
         << "conn.statement_cache ().find_object<object_type> ());";

      if (versioned)
        os << "const schema_version_migration& svm (" <<
          "sts.version_migration (" << schema_name << "));";

      os << endl;

      // Rebind the image if necessary.
      //
      os << "image_type& im (sts.image ());"
         << "binding& imb (sts.select_image_binding (" <<
        (poly_derived ? "depth" : "") << "));"
         << endl;

      if (poly_derived)
      {
        os << "if (imb.version == 0 ||" << endl
           << "check_version (sts.select_image_versions (), im))"
           << "{"
           << "bind (imb.bind, 0, 0, im, statement_select" <<
          (versioned ? ", svm" : "") << ");"
           << "update_version (sts.select_image_versions ()," << endl
           << "im," << endl
           << "sts.select_image_bindings ());"
           << "}";
      }
      else
      {
        os << "if (im.version != sts.select_image_version () ||" << endl
           << "imb.version == 0)"
           << "{"
           << "bind (imb.bind, im, statement_select" <<
          (versioned ? ", svm" : "") << ");"
           << "sts.select_image_version (im.version);"
           << "imb.version++;"
           << "}";
      }

      os << "std::string text (query_statement);"
         << "if (!q.empty ())"
         << "{"
         << "text += " << strlit (sep) << ";"
         << "text += q.clause ();"
         << "}";

      os << "shared_ptr<" << db << "::prepared_query_impl> r (" << endl
         << "new (shared) " << db << "::prepared_query_impl (conn));"
         << "r->name = n;"
         << "r->execute = &execute_query;"
         << "r->query = q;"
         << "r->stmt.reset (" << endl
         << "new (shared) select_statement (" << endl;
      object_query_statement_ctor_args (
        c, "r->query", versioned || query_optimize, true);
      os << "));"
         << endl
         << "return r;"
         << "}";

      // prepare_query(odb::query_base)
      //
      if (multi_dynamic)
        os << "odb::details::shared_ptr<prepared_query_impl>" << endl
           << traits << "::" << endl
           << "prepare_query (connection& c, const char* n, " <<
          "const odb::query_base& q)"
           << "{"
           << "return prepare_query (c, n, query_base_type (q));"
           << "}";

      // execute_query
      //
      os << "odb::details::shared_ptr<result_impl>" << endl
         << traits << "::" << endl
         << "execute_query (prepared_query_impl& q)"
         << "{"
         << "using namespace " << db << ";"
         << "using odb::details::shared;"
         << "using odb::details::shared_ptr;"
         << endl
         << db << "::prepared_query_impl& pq (" << endl
         << "static_cast<" << db << "::prepared_query_impl&> (q));"
         << "shared_ptr<select_statement> st (" << endl
         << "odb::details::inc_ref (" << endl
         << "static_cast<select_statement*> (pq.stmt.get ())));"
         << endl;

      os << db << "::connection& conn (" << endl
         << db << "::transaction::current ().connection ());"
         << endl
         << "// The connection used by the current transaction and the" << endl
         << "// one used to prepare this statement must be the same." << endl
         << "//" << endl
         << "assert (&conn == &st->connection ());"
         << endl
         << "statements_type& sts (" << endl
         << "conn.statement_cache ().find_object<object_type> ());";

      if (versioned)
        os << "const schema_version_migration& svm (" <<
          "sts.version_migration (" << schema_name << "));";

      os << endl;

      // Rebind the image if necessary.
      //
      os << "image_type& im (sts.image ());"
         << "binding& imb (sts.select_image_binding (" <<
        (poly_derived ? "depth" : "") << "));"
         << endl;

      if (poly_derived)
      {
        os << "if (imb.version == 0 ||" << endl
           << "check_version (sts.select_image_versions (), im))"
           << "{"
           << "bind (imb.bind, 0, 0, im, statement_select" <<
          (versioned ? ", svm" : "") << ");"
           << "update_version (sts.select_image_versions ()," << endl
           << "im," << endl
           << "sts.select_image_bindings ());"
           << "}";
      }
      else
      {
        os << "if (im.version != sts.select_image_version () ||" << endl
           << "imb.version == 0)"
           << "{"
           << "bind (imb.bind, im, statement_select" <<
          (versioned ? ", svm" : "") << ");"
           << "sts.select_image_version (im.version);"
           << "imb.version++;"
           << "}";
      }

      os << "pq.query.init_parameters ();"
         << "st->execute ();";
      post_query_ (c, false);

      os << endl
         << "return shared_ptr<result_impl> (" << endl
         << "new (shared) " << db << "::" << result_type << " (" << endl
         << "pq.query, st, sts, " << (versioned ? "&svm" : "0") << "));"
         << "}";
    }
  }

  // Generate function table registration for dynamic multi-database
  // support.
  //
  if (multi_dynamic)
  {
    string fn (flat_name (type));
    string dt ("access::object_traits_impl< " + type + ", id_common >");

    os << "static const" << endl
       << dt << "::" << endl
       << "function_table_type function_table_" << fn << "_ ="
       << "{";

    // persist ()
    //
    os << "&" << traits << "::persist";

    if (id != 0)
    {
      // find (id)
      //
      if (c.default_ctor ())
        os << "," << endl
           << "&" << traits << "::find";

      // find (id, obj)
      //
      os << "," << endl
         << "&" << traits << "::find";

      // reload ()
      //
      os << "," << endl
         << "&" << traits << "::reload";

      // update ()
      //
      if (!readonly || poly)
        os << "," << endl
           << "&" << traits << "::update";

      // erase ()
      //
      os << "," << endl
         << "&" << traits << "::erase";

      os << "," << endl
         << "&" << traits << "::erase";

      // Sections.
      //
      if (uss.count (user_sections::count_total |
                     user_sections::count_load  |
                     (poly ? user_sections::count_load_empty : 0)) != 0)
        os << "," << endl
           << "&" << traits << "::load";

      if (uss.count (user_sections::count_total  |
                     user_sections::count_update |
                     (poly ? user_sections::count_update_empty : 0)) != 0)
        os << "," << endl
           << "&" << traits << "::update";
    }

    if (options.generate_query ())
    {
      if (!options.omit_unprepared ())
        os << "," << endl
           << "&" << traits << "::query";

      os << "," << endl
         << "&" << traits << "::erase_query";

      if (options.generate_prepared ())
      {
        os << "," << endl
           << "&" << traits << "::prepare_query";

        os << "," << endl
           << "&" << traits << "::execute_query";
      }
    }

    os << "};";

    os << "static const object_function_table_entry< " << type << ", " <<
      "id_" << db << " >" << endl
       << "function_table_entry_" << fn << "_ (" << endl
       << "&function_table_" << fn << "_);"
       << endl;
  }
}

void relational::source::class_::
traverse_view (type& c)
{
  view_query& vq (c.get<view_query> ("query"));

  // Only process the view query if it is versioned since the query text
  // (e.g., in the native view) must be structured. We also shouldn't try
  // to optimize JOINs (since the objects are JOINed explicitly by the
  // user), unless we are adding poly-base/derived JOINs.
  //
  bool versioned (context::versioned (c));
  bool query_optimize (false);

  string const& type (class_fq_name (c));
  string traits ("access::view_traits_impl< " + type + ", id_" +
                 db.string () + " >");

  size_t columns (column_count (c).total);

  // Schema name as a string literal or empty.
  //
  string schema_name (options.schema_name ()[db]);
  if (!schema_name.empty ())
    schema_name = strlit (schema_name);

  // Generate the from-list. Also collect relationships via which
  // the objects are joined.
  //
  strings from;
  view_relationship_map rel_map;

  if (vq.kind == view_query::condition)
  {
    view_objects& objs (c.get<view_objects> ("objects"));

    for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
    {
      bool first (i == objs.begin ());
      string l;

      //
      // Tables.
      //

      if (i->kind == view_object::table)
      {
        if (first)
        {
          l = "FROM ";
          l += quote_id (i->tbl_name);

          if (!i->alias.empty ())
            l += (need_alias_as ? " AS " : " ") + quote_id (i->alias);

          l += from_trailer (c);

          from.push_back (l);
          continue;
        }

        l = join_syntax (*i);
        l += ' ';
        l += quote_id (i->tbl_name);

        if (!i->alias.empty ())
          l += (need_alias_as ? " AS " : " ") + quote_id (i->alias);

        if (i->join == view_object::cross) // No ON condition for CROSS JOIN.
        {
          from.push_back (l);
          continue;
        }

        semantics::scope& scope (
          dynamic_cast<semantics::scope&> (*unit.find (i->scope)));

        expression e (
          translate_expression (
            c, i->cond, scope, i->loc, "table"));

        if (e.kind != expression::literal)
        {
          error (i->loc) << "invalid join condition in db pragma " <<
            "table" << endl;
          throw operation_failed ();
        }

        l += " ON";

        // Add the pragma location for easier error tracking.
        //
        from.push_back (l);
        from.push_back ("// From " + location_string (i->loc, true));
        from.push_back (e.value);
        continue;
      }

      //
      // Objects.
      //
      semantics::class_& o (*i->obj);

      bool poly (polymorphic (o));
      size_t poly_depth (poly ? polymorphic_depth (o) : 1);

      string alias (i->alias);

      // For polymorphic objects, alias is just a prefix.
      //
      if (poly && !alias.empty ())
        alias += "_" + table_name (o).uname ();

      // First object.
      //
      if (first)
      {
        l = "FROM ";
        l += table_qname (o);

        if (!alias.empty ())
          l += (need_alias_as ? " AS " : " ") + quote_id (alias);

        l += from_trailer (c);

        from.push_back (l);

        if (poly_depth != 1)
        {
          bool t (true);             //@@ (im)perfect forwarding
          size_t d (poly_depth - 1); //@@ (im)perfect forward.
          instance<polymorphic_object_joins> j (o, t, d, i->alias);
          j->traverse (polymorphic_base (o));

          from.insert (from.end (), j->begin (), j->end ());
          query_optimize = query_optimize || !j->joins.empty ();
        }
        continue;
      }

      semantics::scope& scope (
        dynamic_cast<semantics::scope&> (*unit.find (i->scope)));

      expression e (i->join == view_object::cross
                    ? expression ("") // Dummy literal expression.
                    : translate_expression (
                        c, i->cond, scope, i->loc, "object"));

      // Literal expression.
      //
      if (e.kind == expression::literal)
      {
        l = join_syntax (*i);
        l += ' ';
        l += table_qname (o);

        if (!alias.empty ())
          l += (need_alias_as ? " AS " : " ") + quote_id (alias);

        if (i->join == view_object::cross) // No ON condition for CROSS JOIN.
        {
          from.push_back (l);
          continue;
        }

        l += " ON";

        // Add the pragma location for easier error tracking.
        //
        from.push_back (l);
        from.push_back ("// From " + location_string (i->loc, true));
        from.push_back (e.value);

        if (poly_depth != 1)
        {
          bool t (true);             //@@ (im)perfect forwarding
          size_t d (poly_depth - 1); //@@ (im)perfect forward.
          instance<polymorphic_object_joins> j (o, t, d, i->alias);
          j->traverse (polymorphic_base (o));

          from.insert (from.end (), j->begin (), j->end ());
          query_optimize = query_optimize || !j->joins.empty ();
        }
        continue;
      }

      // We have an object relationship (pointer) for which we need
      // to come up with the corresponding JOIN condition. If this
      // is a to-many relationship, then we first need to JOIN the
      // container table. This code is similar to object_joins.
      //
      // Note that this cannot be CROSS JOIN; we've handled that case
      // above.
      //
      using semantics::data_member;

      data_member& m (*e.member_path.back ());
      data_member* im (inverse (m));

      // Resolve the pointed-to object to view_object and do
      // some sanity checks while at it.
      //
      semantics::class_* c (0);

      if (semantics::type* cont = container (m))
        c = object_pointer (container_vt (*cont));
      else
        c = object_pointer (utype (m));

      view_object* vo (0);

      // Check if the pointed-to object has been previously associated
      // with this view and is unambiguous. A pointer to ourselves is
      // always assumed to point to this association.
      //
      if (&o == c)
        vo = &*i;
      else
      {
        bool ambig (false);

        for (view_objects::iterator j (objs.begin ()); j != i; ++j)
        {
          if (j->obj != c)
            continue;

          if (vo == 0)
          {
            vo = &*j;
            continue;
          }

          // If it is the first ambiguous object, issue the
          // error.
          //
          if (!ambig)
          {
            error (i->loc) << "pointed-to object '" << class_name (*c) <<
              "' is ambiguous" << endl;
            info (i->loc) << "candidates are:" << endl;
            info (vo->loc) << "  '" << vo->name () << "'" << endl;
            ambig = true;
          }

          info (j->loc) << "  '" << j->name () << "'" << endl;
        }

        if (ambig)
        {
          info (i->loc) << "use the other side of the relationship " <<
            "or full join condition clause in db pragma object to " <<
            "resolve this ambiguity" << endl;
          throw operation_failed ();
        }

        if (vo == 0)
        {
          error (i->loc) << "pointed-to object '" << class_name (*c) <<
            "' specified in the join condition has not been " <<
            "previously associated with this view" << endl;
          throw operation_failed ();
        }
      }

      // JOIN relationship points to us:
      //   vo            - us
      //   e.vo          - other side
      //   e.member_path - in other side
      //
      // JOIN relationship points to other side:
      //   vo            - other side
      //   e.vo          - us
      //   e.member_path - in us
      //
      if (im == 0)
        rel_map.insert (make_pair (e.member_path, make_pair (e.vo, vo)));
      else
        rel_map.insert (
          make_pair (data_member_path (*im), make_pair (vo, e.vo)));

      // Left and right-hand side table names.
      //
      qname lt;
      {
        using semantics::class_;

        class_& o (*e.vo->obj);
        string const& a (e.vo->alias);

        if (class_* root = polymorphic (o))
        {
          // If the object is polymorphic, then figure out which of the
          // bases this member comes from and use the corresponding
          // table.
          //
          class_* c (
            &static_cast<class_&> (
              e.member_path.front ()->scope ()));

          // If this member's class is not polymorphic (root uses reuse
          // inheritance), then use the root table.
          //
          if (!polymorphic (*c))
            c = root;

          qname const& t (table_name (*c));

          if (a.empty ())
            lt = t;
          else
            lt = qname (a + "_" + t.uname ());
        }
        else
          lt = a.empty () ? table_name (o) : qname (a);
      }

      qname rt;
      {
        qname t (table_name (*vo->obj));
        string const& a (vo->alias);
        rt = a.empty ()
          ? t
          : qname (polymorphic (*vo->obj) ? a + "_" + t.uname () : a);
      }

      // First join the container table if necessary.
      //
      semantics::type* cont (container (im != 0 ? *im : m));

      string ct; // Container table.
      if (cont != 0)
      {
        l = join_syntax (*i);
        l += ' ';

        // The same relationship can be used by multiple associated
        // objects. So if the object that contains this container has
        // an alias, then also construct one for the table that we
        // are joining.
        //
        {
          using semantics::class_;

          qname t;

          // In a polymorphic hierarchy the member can be in a base (see
          // above).
          //
          if (class_* root = polymorphic (im != 0 ? *vo->obj : *e.vo->obj))
          {
            class_* c;
            if (im == 0)
            {
              c = &static_cast<class_&> (e.member_path.front ()->scope ());

              if (!polymorphic (*c))
                c = root;

              t = table_name (*c, e.member_path);
            }
            else
            {
              c = &static_cast<class_&> (im->scope ());

              if (!polymorphic (*c))
                c = root;

              t = table_name (*im, table_prefix (*c));
            }
          }
          else
            t = im != 0
              ? table_name (*im, table_prefix (*vo->obj))
              : table_name (*e.vo->obj, e.member_path);

          // The tricky part is to figure out which view_object, vo
          // or e.vo we should use. We want to use the alias from the
          // object that actually contains this container. The following
          // might not make intuitive sense, but it has been verified
          // with the truth table.
          //
          string const& a (im != 0 ? vo->alias : e.vo->alias);

          if (a.empty ())
          {
            ct = quote_id (t);
            l += ct;
          }
          else
          {
            ct = quote_id (a + '_' + t.uname ());
            l += quote_id (t);
            l += (need_alias_as ? " AS " : " ") + ct;
          }
        }

        l += " ON";
        from.push_back (l);

        // If we are the pointed-to object, then we have to turn
        // things around. This is necessary to have the proper
        // JOIN order. There seems to be a pattern there but it
        // is not yet intuitively clear what it means.
        //
        instance<object_columns_list> c_cols; // Container columns.
        instance<object_columns_list> o_cols; // Object columns.

        qname* ot; // Object table (either lt or rt).

        if (im != 0)
        {
          if (&o == c)
          {
            // container.value = pointer.id
            //
            semantics::data_member& id (*id_member (*e.vo->obj));

            c_cols->traverse (*im, utype (id), "value", "value");
            o_cols->traverse (id);
            ot = &lt;
          }
          else
          {
            // container.id = pointed-to.id
            //
            semantics::data_member& id (*id_member (*vo->obj));

            c_cols->traverse (
              *im, utype (id), "id", "object_id", vo->obj);
            o_cols->traverse (id);
            ot = &rt;
          }
        }
        else
        {
          if (&o == c)
          {
            // container.id = pointer.id
            //
            semantics::data_member& id (*id_member (*e.vo->obj));

            c_cols->traverse (
              m, utype (id), "id", "object_id", e.vo->obj);
            o_cols->traverse (id);
            ot = &lt;
          }
          else
          {
            // container.value = pointed-to.id
            //
            semantics::data_member& id (*id_member (*vo->obj));

            c_cols->traverse (m, utype (id), "value", "value");
            o_cols->traverse (id);
            ot = &rt;
          }
        }

        for (object_columns_list::iterator b (c_cols->begin ()), i (b),
               j (o_cols->begin ()); i != c_cols->end (); ++i, ++j)
        {
          l.clear ();

          if (i != b)
            l += "AND ";

          l += ct;
          l += '.';
          l += quote_id (i->name);
          l += '=';
          l += quote_id (*ot);
          l += '.';
          l += quote_id (j->name);

          from.push_back (strlit (l));
        }
      }

      // If we have already joined the container with the desired
      // join type, then use LEFT JOIN to join the object to the
      // container. This is the right thing to do since an entry
      // in the container can only point (either via id or value)
      // to a single object.
      //
      l = (cont == 0 ? join_syntax (*i) : "LEFT JOIN");
      l += ' ';

      l += table_qname (o);

      if (!alias.empty ())
        l += (need_alias_as ? " AS " : " ") + quote_id (alias);

      l += " ON";
      from.push_back (l);

      if (cont != 0)
      {
        instance<object_columns_list> c_cols; // Container columns.
        instance<object_columns_list> o_cols; // Object columns.

        qname* ot; // Object table (either lt or rt).

        if (im != 0)
        {
          if (&o == c)
          {
            // container.id = pointed-to.id
            //
            semantics::data_member& id (*id_member (*vo->obj));

            c_cols->traverse (*im, utype (id), "id", "object_id", vo->obj);
            o_cols->traverse (id);
            ot = &rt;
          }
          else
          {
            // container.value = pointer.id
            //
            semantics::data_member& id (*id_member (*e.vo->obj));

            c_cols->traverse (*im, utype (id), "value", "value");
            o_cols->traverse (id);
            ot = &lt;
          }
        }
        else
        {
          if (&o == c)
          {
            // container.value = pointed-to.id
            //
            semantics::data_member& id (*id_member (*vo->obj));

            c_cols->traverse (m, utype (id), "value", "value");
            o_cols->traverse (id);
            ot = &rt;
          }
          else
          {
            // container.id = pointer.id
            //
            semantics::data_member& id (*id_member (*e.vo->obj));

            c_cols->traverse (m, utype (id), "id", "object_id", e.vo->obj);
            o_cols->traverse (id);
            ot = &lt;
          }
        }

        for (object_columns_list::iterator b (c_cols->begin ()), i (b),
               j (o_cols->begin ()); i != c_cols->end (); ++i, ++j)
        {
          l.clear ();

          if (i != b)
            l += "AND ";

          l += ct;
          l += '.';
          l += quote_id (i->name);
          l += '=';
          l += quote_id (*ot);
          l += '.';
          l += quote_id (j->name);

          from.push_back (strlit (l));
        }
      }
      else
      {
        column_prefix col_prefix;

        if (im == 0)
          col_prefix = column_prefix (e.member_path);

        instance<object_columns_list> l_cols (col_prefix);
        instance<object_columns_list> r_cols;

        if (im != 0)
        {
          // our.id = pointed-to.pointer
          //
          l_cols->traverse (*id_member (*e.vo->obj));
          r_cols->traverse (*im);
        }
        else
        {
          // our.pointer = pointed-to.id
          //
          l_cols->traverse (*e.member_path.back ());
          r_cols->traverse (*id_member (*vo->obj));
        }

        for (object_columns_list::iterator b (l_cols->begin ()), i (b),
               j (r_cols->begin ()); i != l_cols->end (); ++i, ++j)
        {
          l.clear ();

          if (i != b)
            l += "AND ";

          l += quote_id (lt);
          l += '.';
          l += quote_id (i->name);
          l += '=';
          l += quote_id (rt);
          l += '.';
          l += quote_id (j->name);

          from.push_back (strlit (l));
        }
      }

      if (poly_depth != 1)
      {
        bool t (true);             //@@ (im)perfect forwarding
        size_t d (poly_depth - 1); //@@ (im)perfect forward.
        instance<polymorphic_object_joins> j (o, t, d, i->alias);
        j->traverse (polymorphic_base (o));

        from.insert (from.end (), j->begin (), j->end ());
        query_optimize = query_optimize || !j->joins.empty ();
      }
    } // End JOIN-generating for-loop.
  }

  // Check that pointed-to objects inside objects that we are loading
  // have session support enabled (required to have a shared copy).
  // Also see if we need to throw if there is no session.
  //
  bool need_session (false);
  if (vq.kind == view_query::condition)
  {
    view_objects& objs (c.get<view_objects> ("objects"));
    for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
    {
      if (i->kind != view_object::object || i->ptr == 0)
        continue; // Not an object or not loaded.

      instance<view_object_check> t (*i, rel_map);
      t->traverse (*i->obj);
      need_session = need_session || t->session_;
    }
  }

  os << "// " << class_name (c) << endl
     << "//" << endl
     << endl;

  view_extra (c);

  // query_columns
  //
  if (c.get<size_t> ("object-count") != 0)
    view_query_columns_type_->traverse (c);

  //
  // Functions.
  //

  // grow ()
  //
  if (generate_grow && columns != 0)
  {
    os << "bool " << traits << "::" << endl
       << "grow (image_type& i," << endl
       << truncated_vector << " t";

    if (versioned)
      os << "," << endl
         << "const schema_version_migration& svm";

    os << ")"
       << "{"
       << "ODB_POTENTIALLY_UNUSED (i);"
       << "ODB_POTENTIALLY_UNUSED (t);";

    if (versioned)
      os << "ODB_POTENTIALLY_UNUSED (svm);";

    os << endl
       << "bool grew (false);"
       << endl;

    index_ = 0;
    names (c, grow_member_names_);

    os << "return grew;"
       << "}";
  }

  // bind (image_type)
  //
  if (columns != 0)
  {
    os << "void " << traits << "::" << endl
       << "bind (" << bind_vector << " b," << endl
       << "image_type& i";

    if (versioned)
      os << "," << endl
         << "const schema_version_migration& svm";

    os << ")"
       << "{";

    if (versioned)
      os << "ODB_POTENTIALLY_UNUSED (svm);"
         << endl;

    os << "using namespace " << db << ";"
       << endl
       << db << "::statement_kind sk (statement_select);"
       << "ODB_POTENTIALLY_UNUSED (sk);"
       << endl
       << "std::size_t n (0);"
       << endl;

    names (c, bind_member_names_);

    os << "}";
  }

  // init (view, image)
  //
  if (columns != 0)
  {
    os << "void " << traits << "::" << endl
       << "init (view_type& o," << endl
       << "const image_type& i," << endl
       << "database* db";

    if (versioned)
      os << "," << endl
         << "const schema_version_migration& svm";

    os << ")"
       << "{"
       << "ODB_POTENTIALLY_UNUSED (o);"
       << "ODB_POTENTIALLY_UNUSED (i);"
       << "ODB_POTENTIALLY_UNUSED (db);";

    if (versioned)
      os << "ODB_POTENTIALLY_UNUSED (svm);";

    os << endl;

    if (need_session)
      os << "if (!session::has_current ())" << endl
         << "throw session_required ();"
         << endl;

    if (has_a (c, test_pointer))
      os << db << "::connection& conn (" << endl
         << db << "::transaction::current ().connection ());"
         << endl;

    names (c, init_view_pointer_member_pre_names_);
    names (c, init_value_member_names_);
    names (c, init_view_pointer_member_post_names_);

    os << "}";
  }

  // query_statement()
  //
  if (vq.kind != view_query::runtime)
  {
    os << traits << "::query_base_type" << endl
       << traits << "::" << endl
       << "query_statement (const query_base_type& q)"
       << "{";

    if (vq.kind == view_query::complete_select ||
        vq.kind == view_query::complete_execute)
    {
      os << "query_base_type r (" << endl;

      bool ph (false);
      bool pred (vq.kind == view_query::complete_select);

      if (!vq.literal.empty ())
      {
        // See if we have the '(?)' placeholder.
        //
        // @@ Ideally we would need to make sure we don't match
        // this inside strings and quoted identifier. So the
        // proper way to handle this would be to tokenize the
        // statement using sql_lexer, once it is complete enough.
        //
        string::size_type p (vq.literal.find ("(?)"));

        if (p != string::npos)
        {
          ph = true;
          // For the SELECT query we keep the parenthesis in (?) and
          // also handle the case where the query expression is empty.
          //
          if (pred)
            os << strlit (string (vq.literal, 0, p + 1)) << " +" << endl
               << "(q.empty () ? query_base_type::true_expr : q) +" << endl
               << strlit (string (vq.literal, p + 2));
          else
          {
            os << strlit (string (vq.literal, 0, p)) << " + q";

            p += 3;
            if (p != vq.literal.size ())
              os << " + " << strlit (string (vq.literal, p));
          }
        }
        else
          os << strlit (vq.literal);
      }
      else
      {
        semantics::scope& scope (
          dynamic_cast<semantics::scope&> (*unit.find (vq.scope)));

        // Output the pragma location for easier error tracking.
        //
        os << "// From " << location_string (vq.loc, true) << endl
           << translate_expression (
             c, vq.expr, scope, vq.loc, "query", &ph, pred).value;
      }

      os << ");";

      // If there was no placeholder, add the query condition
      // at the end.
      //
      if (!ph)
        os << endl
           << "if (!q.empty ())"
           << "{"
           << "r += " << strlit (versioned ? "\n" : " ") << ";"
           << "r += q.clause_prefix ();"
           << "r += q;"
           << "}";
    }
    else // vq.kind == view_query::condition
    {
      // Use the from-list generated above.
      //
      statement_columns sc;
      {
        instance<view_columns> t (sc, from, rel_map);
        t->traverse (c);
        process_statement_columns (
          sc, statement_select, versioned || query_optimize);
      }

      string sep (versioned || query_optimize ? "\n" : " ");

      os << "query_base_type r (" << endl
         << strlit ((vq.distinct ? "SELECT DISTINCT" : "SELECT") + sep);

      for (statement_columns::const_iterator i (sc.begin ()),
             e (sc.end ()); i != e;)
      {
        string const& c (i->column);
        os << endl
           << strlit (c + (++i != e ? "," : "") + sep);
      }

      os << ");"
         << endl;

      // It is much easier to add the separator at the beginning of the
      // next line since the JOIN condition may not be a string literal.
      //
      for (strings::const_iterator i (from.begin ()); i != from.end (); ++i)
      {
        if (i->compare (0, 5, "FROM ") == 0)
          os << "r += " << strlit (*i) << ";";
        else if (i->compare (0, 3, "// ") == 0)
          os << *i << endl;
        else
        {
          // See if this is a JOIN. The exact spelling is database-dependent,
          // but we know there is the JOIN word in there somewhere and before
          // it we should only have keywords and spaces.
          //
          size_t p (i->find ("JOIN "));
          if (p != string::npos)
          {
            // Make sure before it we only have A-Z and spaces.
            //
            for (char c; p != 0; --p)
            {
              c = (*i)[p - 1];
              if ((c < 'A' || c > 'Z') && c != ' ')
                break;
            }

            if (p == 0)
              os << endl
                 << "r += " << strlit (sep + *i) << ";";
          }

          // Something else, assume already a string literal.
          //
          if (p != 0)
            os << "r += " << *i << ";";
        }
      }

      // Generate the query condition.
      //
      if (!vq.literal.empty () || !vq.expr.empty ())
      {
        os << endl
           << "query_base_type c (" << endl;

        bool ph (false);

        if (!vq.literal.empty ())
        {
          // See if we have the '(?)' placeholder.
          //
          // @@ Ideally we would need to make sure we don't match
          // this inside strings and quoted identifier. So the
          // proper way to handle this would be to tokenize the
          // statement using sql_lexer, once it is complete enough.
          //
          string::size_type p (vq.literal.find ("(?)"));

          if (p != string::npos)
          {
            ph = true;
            os << strlit (string (vq.literal, 0, p + 1))<< " +" << endl
               << "(q.empty () ? query_base_type::true_expr : q) +" << endl
               << strlit (string (vq.literal, p + 2));
          }
          else
            os << strlit (vq.literal);

          os << ");";
        }
        else
        {
          semantics::scope& scope (
            dynamic_cast<semantics::scope&> (*unit.find (vq.scope)));

          // Output the pragma location for easier error tracking.
          //
          os << "// From " << location_string (vq.loc, true) << endl
             << translate_expression (
               c, vq.expr, scope, vq.loc, "query", &ph).value;

          os << ");";

          // Optimize the query if it had a placeholder. This gets
          // rid of useless clauses like WHERE TRUE.
          //
          if (ph)
            os << endl
               << "c.optimize ();";
        }

        if (!ph)
          os << endl
             << "c += q;";

        os << endl
           << "if (!c.empty ())"
           << "{"
           << "r += " << strlit (sep) << ";"
           << "r += c.clause_prefix ();"
           << "r += c;"
           << "}";

        string st (select_trailer (c));
        if (!st.empty ())
        {
          os << "r += " << strlit (sep) << ";"
             << "r += " << strlit (st) << ";";
        }
      }
      else
      {
        os << endl
           << "if (!q.empty ())"
           << "{"
           << "r += " << strlit (sep) << ";"
           << "r += q.clause_prefix ();"
           << "r += q;"
           << "}";
      }
    }

    os << "return r;"
       << "}";
  }

  // Unprepared.
  //
  if (!options.omit_unprepared ())
  {
    os << "result< " << traits << "::view_type >" << endl
       << traits << "::" << endl
       << "query (database&, const query_base_type& q)"
       << "{"
       << "using namespace " << db << ";"
       << "using odb::details::shared;"
       << "using odb::details::shared_ptr;"
       << endl;

    os << db << "::connection& conn (" << endl
       << db << "::transaction::current ().connection ());"
       << "statements_type& sts (" << endl
       << "conn.statement_cache ().find_view<view_type> ());";

    if (versioned)
      os << "const schema_version_migration& svm (" <<
        "sts.version_migration (" << schema_name << "));";

    os << endl
       << "image_type& im (sts.image ());"
       << "binding& imb (sts.image_binding ());"
       << endl
       << "if (im.version != sts.image_version () || imb.version == 0)"
       << "{"
       << "bind (imb.bind, im" << (versioned ? ", svm" : "") << ");"
       << "sts.image_version (im.version);"
       << "imb.version++;"
       << "}";

    if (vq.kind == view_query::runtime)
      os << "const query_base_type& qs (q);";
    else
      os << "const query_base_type& qs (query_statement (q));";

    os << "qs.init_parameters ();"
       << "shared_ptr<select_statement> st (" << endl
       << "new (shared) select_statement (" << endl;
    view_query_statement_ctor_args (
      c, "qs", versioned || query_optimize, false);
    os << "));" << endl
       << "st->execute ();";

    post_query_ (c, true);

    os << endl
       << "shared_ptr< odb::view_result_impl<view_type> > r (" << endl
       << "new (shared) " << db << "::view_result_impl<view_type> (" << endl
       << "qs, st, sts, " << (versioned ? "&svm" : "0") << "));"
       << endl
       << "return result<view_type> (r);"
       << "}";

    // query(odb::query_base)
    //
    if (multi_dynamic)
      os << "result< " << traits << "::view_type >" << endl
         << traits << "::" << endl
         << "query (database& db, const odb::query_base& q)"
         << "{"
         << "return query (db, query_base_type (q));"
         << "}";
  }

  // Prepared. Very similar to unprepared but has some annoying variations
  // that make it difficult to factor out something common.
  //
  if (options.generate_prepared ())
  {
    // prepare_query
    //
    os << "odb::details::shared_ptr<prepared_query_impl>" << endl
       << traits << "::" << endl
       << "prepare_query (connection& c, const char* n, " <<
      "const query_base_type& q)"
       << "{"
       << "using namespace " << db << ";"
       << "using odb::details::shared;"
       << "using odb::details::shared_ptr;"
       << endl;

    os << db << "::connection& conn (" << endl
       << "static_cast<" << db << "::connection&> (c));"
       << "statements_type& sts (" << endl
       << "conn.statement_cache ().find_view<view_type> ());";

    if (versioned)
      os << "const schema_version_migration& svm (" <<
        "sts.version_migration (" << schema_name << "));";

    os << endl;

    // Rebind the image if necessary.
    //
    os << "image_type& im (sts.image ());"
       << "binding& imb (sts.image_binding ());"
       << endl
       << "if (im.version != sts.image_version () || imb.version == 0)"
       << "{"
       << "bind (imb.bind, im" << (versioned ? ", svm" : "") << ");"
       << "sts.image_version (im.version);"
       << "imb.version++;"
       << "}";

    os << "shared_ptr<" << db << "::prepared_query_impl> r (" << endl
       << "new (shared) " << db << "::prepared_query_impl (conn));"
       << "r->name = n;"
       << "r->execute = &execute_query;";

    if (vq.kind == view_query::runtime)
      os << "r->query = q;";
    else
      os << "r->query = query_statement (q);";

    os << "r->stmt.reset (" << endl
       << "new (shared) select_statement (" << endl;
    view_query_statement_ctor_args (
      c, "r->query", versioned || query_optimize, true);
    os << "));"
       << endl
       << "return r;"
       << "}";

    // prepare_query(odb::query_base)
    //
    if (multi_dynamic)
      os << "odb::details::shared_ptr<prepared_query_impl>" << endl
         << traits << "::" << endl
         << "prepare_query (connection& c, const char* n, " <<
        "const odb::query_base& q)"
         << "{"
         << "return prepare_query (c, n, query_base_type (q));"
         << "}";

    // execute_query
    //
    os << "odb::details::shared_ptr<result_impl>" << endl
       << traits << "::" << endl
       << "execute_query (prepared_query_impl& q)"
       << "{"
       << "using namespace " << db << ";"
       << "using odb::details::shared;"
       << "using odb::details::shared_ptr;"
       << endl
       << db << "::prepared_query_impl& pq (" << endl
       << "static_cast<" << db << "::prepared_query_impl&> (q));"
       << "shared_ptr<select_statement> st (" << endl
       << "odb::details::inc_ref (" << endl
       << "static_cast<select_statement*> (pq.stmt.get ())));"
       << endl;

    os << db << "::connection& conn (" << endl
       << db << "::transaction::current ().connection ());"
       << endl
       << "// The connection used by the current transaction and the" << endl
       << "// one used to prepare this statement must be the same." << endl
       << "//" << endl
       << "assert (&conn == &st->connection ());"
       << endl
       << "statements_type& sts (" << endl
       << "conn.statement_cache ().find_view<view_type> ());";

    if (versioned)
      os << "const schema_version_migration& svm (" <<
        "sts.version_migration (" << schema_name << "));";

    os << endl;

    // Rebind the image if necessary.
    //
    os << "image_type& im (sts.image ());"
       << "binding& imb (sts.image_binding ());"
       << endl
       << "if (im.version != sts.image_version () || imb.version == 0)"
       << "{"
       << "bind (imb.bind, im" << (versioned ? ", svm" : "") << ");"
       << "sts.image_version (im.version);"
       << "imb.version++;"
       << "}";

    os << "pq.query.init_parameters ();"
       << "st->execute ();";

    post_query_ (c, false);

    os << endl
       << "return shared_ptr<result_impl> (" << endl
       << "new (shared) " << db << "::view_result_impl<view_type> (" << endl
       << "pq.query, st, sts, " << (versioned ? "&svm" : "0") << "));"
       << "}";
  }

  // Generate function table registration for dynamic multi-database
  // support.
  //
  if (multi_dynamic)
  {
    string fn (flat_name (type));
    string dt ("access::view_traits_impl< " + type + ", id_common >");

    os << "static const" << endl
       << dt << "::" << endl
       << "function_table_type function_table_" << fn << "_ ="
       << "{";

    if (!options.omit_unprepared ())
      os << "&" << traits << "::query";

    if (options.generate_prepared ())
    {
      if (!options.omit_unprepared ())
        os << "," << endl;

      os << "&" << traits << "::prepare_query" << "," << endl
         << "&" << traits << "::execute_query";
    }

    os << "};";

    os << "static const view_function_table_entry< " << type << ", " <<
      "id_" << db << " >" << endl
       << "function_table_entry_" << fn << "_ (" << endl
       << "&function_table_" << fn << "_);"
       << endl;
  }
}

namespace relational
{
  namespace source
  {
    static inline void
    add_space (string& s)
    {
      string::size_type n (s.size ());
      if (n != 0 && s[n - 1] != ' ')
        s += ' ';
    }

    static string
    translate_name_trailer (cxx_lexer& l,
                            cpp_ttype& tt,
                            string& tl,
                            tree& tn,
                            cpp_ttype& ptt)
    {
      string r;

      for (; tt != CPP_EOF; ptt = tt, tt = l.next (tl, &tn))
      {
        bool done (false);

        switch (tt)
        {
        case CPP_SCOPE:
        case CPP_DOT:
          {
            r += cxx_lexer::token_spelling[tt];
            break;
          }
        default:
          {
            // Handle CPP_KEYWORD here to avoid a warning (it is not
            // part of the cpp_ttype enumeration).
            //
            if (tt == CPP_NAME || tt == CPP_KEYWORD)
            {
              // For names like 'foo::template bar'.
              //
              if (ptt == CPP_NAME || ptt == CPP_KEYWORD)
                r += ' ';

              r += tl;
            }
            else
              done = true;

            break;
          }
        }

        if (done)
          break;
      }

      return r;
    }

    static class_::expression
    translate_name (cxx_lexer& l,
                    cpp_ttype& tt,
                    string& tl,
                    tree& tn,
                    cpp_ttype& ptt,
                    semantics::scope& start_scope,
                    location_t loc,
                    string const& prag,
                    bool check_ptr,
                    view_alias_map const& amap,
                    view_object_map const& omap)
    {
      using semantics::scope;
      using semantics::data_member;
      typedef class_::expression expression;

      bool multi_obj ((amap.size () + omap.size ()) > 1);

      bool fail (false);
      string name;
      string r ("query_columns");
      context& ctx (context::current ());

      // This code is quite similar to view_data_members in the type
      // processor.
      //
      try
      {
        data_member* m (0);
        view_object* vo (0);

        // Check if this is an alias.
        //
        if (tt == CPP_NAME)
        {
          view_alias_map::const_iterator i (amap.find (tl));

          if (i != amap.end ())
          {
            if (multi_obj)
            {
              r += "::";
              r += i->first;
            }

            vo = i->second;
            fail = true; // This must be a data member.

            // Skip '::'.
            //
            ptt = tt;
            tt = l.next (tl, &tn);

            if (tt != CPP_SCOPE)
            {
              error (loc) << "member name expected after an alias in db " <<
                "pragma " << prag << endl;
              throw operation_failed ();
            }

            ptt = tt;
            if (l.next (tl, &tn) != CPP_NAME)
              throw lookup::invalid_name ();

            m = &vo->obj->lookup<data_member> (tl, scope::include_hidden);

            tt = l.next (tl, &tn);
          }
        }

        // If it is not an alias, do the normal lookup.
        //
        if (vo == 0)
        {
          // Also get the object type. We need to do it so that
          // we can get the correct (derived) object name (the
          // member itself can come from a base class).
          //
          scope* s;
          cpp_ttype ptt; // Not used.
          m = &lookup::resolve_scoped_name<data_member> (
            l, tt, tl, tn, ptt,
            start_scope,
            name,
            false,
            &s);

          view_object_map::const_iterator i (
            omap.find (dynamic_cast<semantics::class_*> (s)));

          if (i == omap.end ())
          {
            // Not an object associated with this view. Assume it
            // is some other valid name.
            //
            return expression (
              name + translate_name_trailer (l, tt, tl, tn, ptt));
          }

          vo = i->second;

          if (multi_obj)
          {
            r += "::";
            r += context::class_name (*vo->obj);
          }
        }

        expression e (vo);
        r += "::";
        r += ctx.public_name (*m);

        // Assemble the member path if we may need to return a pointer
        // expression.
        //
        if (check_ptr)
          e.member_path.push_back (m);

        fail = true; // Now we definitely fail if anything goes wrong.

        // Finally, resolve nested members if any.
        //
        for (; tt == CPP_DOT; ptt = tt, tt = l.next (tl, &tn))
        {
          // Check if this member is actually of a composite value type.
          // This is to handle expressions like "object::member.is_null ()"
          // correctly. The remaining issue here is that in the future
          // is_null()/is_not_null() will be valid for composite values
          // as well.
          //
          semantics::class_* comp (
            context::composite_wrapper (context::utype (*m)));
          if (comp == 0)
            break;

          ptt = tt;
          tt = l.next (tl, &tn);

          if (tt != CPP_NAME)
          {
            error (loc) << "name expected after '.' in db pragma " <<
              prag << endl;
            throw operation_failed ();
          }

          m = &comp->lookup<data_member> (tl, scope::include_hidden);

          r += '.';
          r += ctx.public_name (*m);

          if (check_ptr)
            e.member_path.push_back (m);
        }

        // If requested, check if this member is a pointer. We only do this
        // if there is nothing after this name.
        //
        if (check_ptr && tt == CPP_EOF)
        {
          using semantics::type;

          type* t;

          if (type* c = context::container (*m))
            t = &context::container_vt (*c);
          else
            t = &context::utype (*m);

          if (context::object_pointer (*t))
            return e;
        }

        // Read the remainder of the expression (e.g., '.is_null ()') if
        // the member is not composite and we bailed out from the above
        // loop.
        //
        if (tt == CPP_DOT)
          r += translate_name_trailer (l, tt, tl, tn, ptt);

        return expression (r);
      }
      catch (lookup::invalid_name const&)
      {
        if (!fail)
          return expression (
            name + translate_name_trailer (l, tt, tl, tn, ptt));

        error (loc) << "invalid name in db pragma " << prag << endl;
        throw operation_failed ();
      }
      catch (semantics::unresolved const& e)
      {
        if (!fail)
          return expression (
            name + translate_name_trailer (l, tt, tl, tn, ptt));

        if (e.type_mismatch)
          error (loc) << "name '" << e.name << "' in db pragma " << prag <<
            " does not refer to a data member" << endl;
        else
          error (loc) << "unable to resolve data member '" << e.name <<
            "' specified with db pragma " << prag << endl;

        throw operation_failed ();
      }
      catch (semantics::ambiguous const& e)
      {
        error (loc) << "data member name '" << e.first.name () << "' " <<
          "specified with db pragma " << prag << " is ambiguous" << endl;

        info (e.first.named ().location ()) << "could resolve to this " <<
          "data member" << endl;

        info (e.second.named ().location ()) << "or could resolve to this " <<
          "data member" << endl;

        throw operation_failed ();
      }
    }

    class_::expression class_::
    translate_expression (type& c,
                          cxx_tokens const& ts,
                          semantics::scope& scope,
                          location_t loc,
                          string const& prag,
                          bool* placeholder,
                          bool predicate)
    {
      // This code is similar to translate() from context.cxx.
      //

      // The overall idea is as folows: read in tokens and add them
      // to the string. If a token starts a name, try to resolve it
      // to an object member (taking into account aliases). If this
      // was successful, translate it to the query column reference.
      // Otherwise, output it as is.
      //
      // If the placeholder argument is not NULL, then we need to
      // detect the special '(?)' token sequence and replace it
      // with the query variable ('q').
      //
      expression e ("");
      string& r (e.value);

      view_alias_map const& amap (c.get<view_alias_map> ("alias-map"));
      view_object_map const& omap (c.get<view_object_map> ("object-map"));

      cxx_tokens_lexer l;
      l.start (ts);

      tree tn;
      string tl;
      for (cpp_ttype tt (l.next (tl, &tn)), ptt (CPP_EOF); tt != CPP_EOF;)
      {
        // Try to format the expression to resemble the style of the
        // generated code.
        //
        switch (tt)
        {
        case CPP_NOT:
          {
            add_space (r);
            r += '!';
            break;
          }
        case CPP_COMMA:
          {
            r += ", ";
            break;
          }
        case CPP_OPEN_PAREN:
          {
            if (ptt == CPP_NAME ||
                ptt == CPP_KEYWORD)
              add_space (r);

            r += '(';
            break;
          }
        case CPP_CLOSE_PAREN:
          {
            r += ')';
            break;
          }
        case CPP_OPEN_SQUARE:
          {
            r += '[';
            break;
          }
        case CPP_CLOSE_SQUARE:
          {
            r += ']';
            break;
          }
        case CPP_OPEN_BRACE:
          {
            add_space (r);
            r += "{ ";
            break;
          }
        case CPP_CLOSE_BRACE:
          {
            add_space (r);
            r += '}';
            break;
          }
        case CPP_SEMICOLON:
          {
            r += ';';
            break;
          }
        case CPP_ELLIPSIS:
          {
            add_space (r);
            r += "...";
            break;
          }
        case CPP_PLUS:
        case CPP_MINUS:
          {
            bool unary (ptt != CPP_NAME &&
                        ptt != CPP_SCOPE &&
                        ptt != CPP_NUMBER &&
                        ptt != CPP_STRING &&
                        ptt != CPP_CLOSE_PAREN &&
                        ptt != CPP_PLUS_PLUS &&
                        ptt != CPP_MINUS_MINUS);

            if (!unary)
              add_space (r);

            r += cxx_lexer::token_spelling[tt];

            if (!unary)
              r += ' ';
            break;
          }
        case CPP_PLUS_PLUS:
        case CPP_MINUS_MINUS:
          {
            if (ptt != CPP_NAME &&
                ptt != CPP_CLOSE_PAREN &&
                ptt != CPP_CLOSE_SQUARE)
              add_space (r);

            r += cxx_lexer::token_spelling[tt];
            break;
          }
        case CPP_DEREF:
        case CPP_DEREF_STAR:
        case CPP_DOT:
        case CPP_DOT_STAR:
          {
            r += cxx_lexer::token_spelling[tt];
            break;
          }
        case CPP_STRING:
          {
            if (ptt == CPP_NAME ||
                ptt == CPP_KEYWORD ||
                ptt == CPP_STRING ||
                ptt == CPP_NUMBER)
              add_space (r);

            r += strlit (tl);
            break;
          }
        case CPP_NUMBER:
          {
            if (ptt == CPP_NAME ||
                ptt == CPP_KEYWORD ||
                ptt == CPP_STRING ||
                ptt == CPP_NUMBER)
              add_space (r);

            r += tl;
            break;
          }
        case CPP_SCOPE:
        case CPP_NAME:
          {
            // Start of a name.
            //
            if (ptt == CPP_NAME ||
                ptt == CPP_KEYWORD ||
                ptt == CPP_STRING ||
                ptt == CPP_NUMBER)
              add_space (r);

            // Check if this is a pointer expression.
            //
            // If r is not empty, then it means this is not just the
            // name. If placeholder is not 0, then we are translating
            // a query expression, not a join condition.
            //
            expression e (
              translate_name (
                l, tt, tl, tn, ptt,
                scope, loc, prag,
                r.empty () && placeholder == 0, amap, omap));

            if (e.kind == expression::literal)
              r += e.value;
            else
              return e;

            continue; // We have already extracted the next token.
          }
        case CPP_QUERY:
          {
            if (placeholder != 0 && !*placeholder)
            {
              if (ptt == CPP_OPEN_PAREN)
              {
                // Get the next token and see if it is ')'.
                //
                ptt = tt;
                tt = l.next (tl, &tn);

                if (tt == CPP_CLOSE_PAREN)
                {
                  *placeholder = true;

                  // Predicate is true if this is a SELECT statement clause.
                  // Otherwise it is a stored procedure parameters.
                  //
                  if (predicate)
                    r +=  "q.empty () ? query_base_type::true_expr : q";
                  else
                  {
                    r.resize (r.size () - 1); // Remove opening paren.
                    r += "q";
                    break; // Skip the closing paren as well.
                  }
                }
                else
                {
                  // The same as in the default case.
                  //
                  add_space (r);
                  r += "? ";
                }
                continue; // We have already gotten the next token.
              }
            }
            // Fall through.
          }
        default:
          {
            // Handle CPP_KEYWORD here to avoid a warning (it is not
            // part of the cpp_ttype enumeration).
            //
            if (tt == CPP_KEYWORD)
            {
              if (ptt == CPP_NAME ||
                  ptt == CPP_KEYWORD ||
                  ptt == CPP_STRING ||
                  ptt == CPP_NUMBER)
                add_space (r);

              r += tl;
            }
            else
            {
              // All the other operators.
              //
              add_space (r);
              r += cxx_lexer::token_spelling[tt];
              r += ' ';
            }
            break;
          }
        }

        //
        // Watch out for the continue statements above if you add any
        // logic here.
        //

        ptt = tt;
        tt = l.next (tl, &tn);
      }

      return e;
    }

    void
    generate ()
    {
      context ctx;
      ostream& os (ctx.os);

      traversal::unit unit;
      traversal::defines unit_defines;
      typedefs unit_typedefs (false);
      traversal::namespace_ ns;
      instance<class_> c;

      unit >> unit_defines >> ns;
      unit_defines >> c;
      unit >> unit_typedefs >> c;

      traversal::defines ns_defines;
      typedefs ns_typedefs (false);

      ns >> ns_defines >> ns;
      ns_defines >> c;
      ns >> ns_typedefs >> c;

      instance<include> i;
      i->generate ();

      os << "namespace odb"
         << "{";

      unit.dispatch (ctx.unit);

      os << "}";
    }
  }
}
