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

#include <odb/gcc.hxx>

#include <vector>
#include <algorithm>

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

#include <odb/relational/context.hxx>
#include <odb/relational/processor.hxx>

using namespace std;

namespace relational
{
  namespace
  {
    // Indirect (dynamic) context values.
    //
    static string
    id_column_type ()
    {
      context& c (context::current ());
      semantics::data_member& id (*context::id_member (*c.top_object));
      return id.get<string> ("column-type");
    }

    struct data_member: traversal::data_member, context
    {
      virtual void
      traverse (semantics::data_member& m)
      {
        if (transient (m))
          return;

        semantics::names* hint;
        semantics::type& t (utype (m, hint));

        semantics::type* wt;
        semantics::names* whint (0);
        if ((wt = wrapper (t, whint)))
          wt = &utype (*wt, whint);

        // Determine the member kind.
        //
        enum {simple, composite, container, unknown} kind (unknown);

        // See if this is a composite value type.
        //
        if (composite_wrapper (t))
          kind = composite;

        // If not, see if it is a simple value.
        //
        if (kind == unknown)
        {
          string type, id_type;

          if (m.count ("id-type"))
            id_type = m.get<string> ("id-type");

          if (m.count ("type"))
          {
            type = m.get<string> ("type");

            if (id_type.empty ())
              id_type = type;
          }

          if (semantics::class_* c = object_pointer (t))
          {
            // An object pointer in view doesn't really have a "column"
            // so pretend that it has already been handled.
            //
            if (view_member (m))
              kind = simple;
            else
            {
              // This is an object pointer. The column type is the pointed-to
              // object id type.
              //
              semantics::data_member& id (*id_member (*c));

              semantics::names* idhint;
              semantics::type& idt (utype (id, idhint));

              // The id type can be a composite value type.
              //
              if (composite_wrapper (idt))
                kind = composite;
              else
              {
                semantics::type* wt;
                semantics::names* whint (0);
                if ((wt = wrapper (idt, whint)))
                  wt = &utype (*wt, whint);

                if (type.empty () && id.count ("id-type"))
                  type = id.get<string> ("id-type");

                if (type.empty () && id.count ("type"))
                  type = id.get<string> ("type");

                // The rest should be identical to the code for the id_type in
                // the else block.
                //
                if (type.empty () && idt.count ("id-type"))
                  type = idt.get<string> ("id-type");

                if (type.empty () && wt != 0 && wt->count ("id-type"))
                  type = wt->get<string> ("id-type");

                if (type.empty () && idt.count ("type"))
                  type = idt.get<string> ("type");

                if (type.empty () && wt != 0 && wt->count ("type"))
                  type = wt->get<string> ("type");

                if (type.empty ())
                  type = database_type (idt, idhint, true);

                if (type.empty () && wt != 0)
                  type = database_type (*wt, whint, true);

                id_type = type;
              }
            }
          }
          else
          {
            if (id_type.empty () && t.count ("id-type"))
              id_type = t.get<string> ("id-type");

            if (id_type.empty () && wt != 0 && wt->count ("id-type"))
              id_type = wt->get<string> ("id-type");

            if (type.empty () && t.count ("type"))
              type = t.get<string> ("type");

            if (type.empty () && wt != 0 && wt->count ("type"))
              type = wt->get<string> ("type");

            if (id_type.empty ())
              id_type = type;

            if (id_type.empty ())
              id_type = database_type (t, hint, true);

            if (id_type.empty () && wt != 0)
              id_type = database_type (*wt, whint, true);

            bool null (false);
            if (type.empty ())
              type = database_type (t, hint, false, &null);

            if (type.empty () && wt != 0)
              type = database_type (*wt, whint, false, &null);

            // Use id mapping for discriminators.
            //
            if (id (m) || discriminator (m))
              type = id_type;
            // Allow NULL if requested by the default mapping.
            //
            else if (null && !m.count ("not-null"))
              m.set ("null", true);
          }

          if (kind == unknown && !type.empty ())
          {
            m.set ("column-type", type);
            m.set ("column-id-type", id_type);

            // Issue a warning if we are relaxing null-ness.
            //
            if (m.count ("null") && t.count ("not-null"))
            {
              os << m.file () << ":" << m.line () << ":" << m.column () << ":"
                 << " warning: data member declared null while its type is "
                 << "declared not null" << endl;
            }

            kind = simple;
          }
        }

        // If not a simple value, see if this is a container.
        //
        if (kind == unknown && context::container (m))
        {
          process_container (m, (wt != 0 ? *wt : t));
          kind = container;
        }

        // If it is none of the above then we have an error.
        //
        if (kind == unknown)
        {
          os << m.file () << ":" << m.line () << ":" << m.column () << ":"
             << " error: unable to map C++ type '" << t.fq_name (hint)
             << "' used in data member '" << m.name () << "' to a "
             << db.name () << " database type" << endl;

          os << m.file () << ":" << m.line () << ":" << m.column () << ":"
             << " info: use '#pragma db type' to specify the database type"
             << endl;

          throw operation_failed ();
        }

        if (m.count ("polymorphic-ref"))
        {
          // Copy the column name from the root's id member, if specified.
          //
          {
            semantics::class_& r (*object_pointer (t));
            semantics::data_member& id (*id_member (r));

            if (id.count ("column"))
              m.set ("column", id.get<table_column> ("column"));
          }

          m.set ("not-null", true);
          m.set ("deferrable",
                 sema_rel::deferrable (sema_rel::deferrable::not_deferrable));
          m.set ("on-delete", sema_rel::foreign_key::cascade);
        }

        process_index (m);
      }

      // Convert index/unique specifiers to the index entry in the object.
      //
      void
      process_index (semantics::data_member& m)
      {
        bool ip (m.count ("index"));
        bool up (m.count ("unique"));

        if (ip || up)
        {
          using semantics::class_;
          class_& c (dynamic_cast<class_&> (m.scope ()));

          indexes& ins (c.count ("index")
                        ? c.get<indexes> ("index")
                        : c.set ("index", indexes ()));

          index in;
          in.loc = m.get<location_t> (
            ip ? "index-location" : "unique-location");

          if (up)
            in.type = "UNIQUE";

          index::member im;
          im.loc = in.loc;
          im.name = m.name ();
          im.path.push_back (&m);
          in.members.push_back (im);

          // Insert it in the location order.
          //
          ins.insert (
            lower_bound (ins.begin (), ins.end (), in, index_comparator ()),
            in);
        }
      }

      void
      process_container_value (semantics::type& t,
                               semantics::names* hint,
                               semantics::data_member& m,
                               string const& prefix,
                               bool obj_ptr)
      {
        if (composite_wrapper (t))
          return;

        semantics::names* wh (0);
        semantics::type* wt (wrapper (t, wh));

        string type;
        semantics::type& ct (utype (m));

        // Custom mapping can come from these places (listed in the order
        // of priority): member, container type, value type. To complicate
        // things a bit, for object references, it can also come from the
        // member and value type of the id member.
        //
        if (m.count (prefix + "-type"))
          type = m.get<string> (prefix + "-type");

        if (type.empty () && ct.count (prefix + "-type"))
          type = ct.get<string> (prefix + "-type");

        semantics::class_* c;
        if (obj_ptr && (c = object_pointer (t)))
        {
          // This is an object pointer. The column type is the pointed-to
          // object id type.
          //
          semantics::data_member& id (*id_member (*c));

          semantics::names* idhint;
          semantics::type& idt (utype (id, idhint));

          // Nothing to do if this is a composite value type.
          //
          if (composite_wrapper (idt))
            return;

          semantics::type* wt (0);
          semantics::names* whint (0);
          if ((wt = wrapper (idt, whint)))
            wt = &utype (*wt, whint);

          if (type.empty () && id.count ("id-type"))
            type = id.get<string> ("id-type");

          if (type.empty () && id.count ("type"))
            type = id.get<string> ("type");

          // The rest of the code is identical to the else block except here
          // we have to check for "id-type" before checking for "type".
          //

          if (type.empty () && idt.count ("id-type"))
            type = idt.get<string> ("id-type");

          if (type.empty () && wt != 0 && wt->count ("id-type"))
            type = wt->get<string> ("id-type");

          if (type.empty () && idt.count ("type"))
            type = idt.get<string> ("type");

          if (type.empty () && wt != 0 && wt->count ("type"))
            type = wt->get<string> ("type");

          if (type.empty ())
            type = database_type (idt, idhint, true);

          if (type.empty () && wt != 0)
            type = database_type (*wt, whint, true);
        }
        else
        {
          if (type.empty () && t.count ("type"))
            type = t.get<string> ("type");

          if (type.empty () && wt != 0 && wt->count ("type"))
            type = wt->get<string> ("type");

          bool null (false);
          if (type.empty ())
            type = database_type (t, hint, false, &null);

          if (type.empty () && wt != 0)
            type = database_type (*wt, wh, false, &null);

          // Allow NULL if requested by the default mapping.
          //
          if (null && !m.count (prefix + "-not-null"))
            m.set (prefix + "-null", true);
        }

        if (!type.empty ())
        {
          m.set (prefix + "-column-type", type);
          m.set (prefix + "-column-id-type", type);
          return;
        }

        // We do not support nested containers so skip that test.
        //

        // If it is none of the above then we have an error.
        //
        string fq_type (t.fq_anonymous () ? "<anonymous>" : t.fq_name ());

        os << m.file () << ":" << m.line () << ":" << m.column () << ":"
           << " error: unable to map C++ type '" << fq_type << "' used in "
           << "data member '" << m.name () << "' to a " << db.name ()
           << " database type" << endl;

        os << m.file () << ":" << m.line () << ":" << m.column () << ":"
           << " info: use '#pragma db " << prefix << "_type' to specify the "
           << "database type" << endl;

        throw operation_failed ();
      }

      void
      process_container (semantics::data_member& m, semantics::type& t)
      {
        container_kind_type ck (t.get<container_kind_type> ("container-kind"));
        semantics::type* vt (t.get<semantics::type*> ("value-tree-type"));
        semantics::type* it (0);
        semantics::type* kt (0);

        semantics::names* vh (t.get<semantics::names*> ("value-tree-hint"));
        semantics::names* ih (0);
        semantics::names* kh (0);

        if (ck == ck_ordered)
        {
          it = t.get<semantics::type*> ("index-tree-type");
          ih = t.get<semantics::names*> ("index-tree-hint");
        }

        if (ck == ck_map || ck == ck_multimap)
        {
          kt = t.get<semantics::type*> ("key-tree-type");
          kh = t.get<semantics::names*> ("key-tree-hint");
        }

        // Process member data.
        //
        m.set ("id-column-type", &id_column_type);

        process_container_value (*vt, vh, m, "value", true);

        if (it != 0)
          process_container_value (*it, ih, m, "index", false);

        if (kt != 0)
          process_container_value (*kt, kh, m, "key", false);
      }
    };

    //
    //
    struct view_data_member: traversal::data_member, context
    {
      view_data_member (semantics::class_& c)
          : view_ (c),
            query_ (c.get<view_query> ("query")),
            amap_ (c.get<view_alias_map> ("alias-map")),
            omap_ (c.get<view_object_map> ("object-map"))
      {
      }

      struct assoc_member
      {
        semantics::data_member* m;
        view_object* vo;
      };

      typedef vector<assoc_member> assoc_members;

      virtual void
      traverse (semantics::data_member& m)
      {
        using semantics::data_member;

        if (transient (m))
          return;

        semantics::type& t (utype (m));

        // Object pointers are associated with objects.
        //
        if (object_pointer (t))
          return;

        data_member* src_m (0); // Source member.

        // Resolve member references in column expressions.
        //
        if (m.count ("column"))
        {
          // Column literal.
          //
          if (query_.kind != view_query::condition)
          {
            warn (m.get<location_t> ("column-location"))
              << "db pragma column ignored in a view with "
              << (query_.kind == view_query::runtime ? "runtime" : "complete")
              << " query" << endl;
          }

          return;
        }
        else if (m.count ("column-expr"))
        {
          column_expr& e (m.get<column_expr> ("column-expr"));

          if (query_.kind != view_query::condition)
          {
            warn (e.loc)
              << "db pragma column ignored in a view with "
              << (query_.kind == view_query::runtime ? "runtime" : "complete")
              << " query" << endl;
            return;
          }

          for (column_expr::iterator i (e.begin ()); i != e.end (); ++i)
          {
            // This code is quite similar to translate_expression in the
            // source generator.
            //
            try
            {
              using semantics::scope;
              using semantics::class_;

              if (i->kind != column_expr_part::reference)
                continue;

              lex_.start (i->value);

              string tl;
              tree tn;
              cpp_ttype tt (lex_.next (tl, &tn));

              data_member* m (0);
              view_object* vo (0);

              // Check if this is an alias.
              //
              if (tt == CPP_NAME)
              {
                view_alias_map::iterator j (amap_.find (tl));

                if (j != amap_.end ())
                {
                  vo = j->second;

                  // Skip '::'.
                  //
                  if (lex_.next (tl, &tn) != CPP_SCOPE)
                  {
                    error (i->loc) << "member name expected after an alias " <<
                      "in db pragma column" << endl;
                    throw operation_failed ();
                  }

                  if (lex_.next (tl, &tn) != CPP_NAME)
                    throw lookup::invalid_name ();

                  m = &vo->obj->lookup<data_member> (
                    tl, scope::include_hidden);

                  tt = lex_.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) table name (the
                // member itself can come from a base class).
                //
                scope* s;
                string name;
                cpp_ttype ptt; // Not used.
                m = &lookup::resolve_scoped_name<data_member> (
                  lex_, tt, tl, tn, ptt,
                  dynamic_cast<scope&> (*unit.find (i->scope)),
                  name,
                  false,
                  &s);

                view_object_map::iterator j (
                  omap_.find (dynamic_cast<class_*> (s)));

                if (j == omap_.end ())
                {
                  error (i->loc) << "name '" << name << "' in db pragma " <<
                    "column does not refer to a data member of a " <<
                    "persistent class that is used in this view" << endl;
                  throw operation_failed ();
                }

                vo = j->second;
              }

              i->member_path.push_back (m);

              // Figure out the table name/alias for this member.
              //
              if (class_* root = polymorphic (*vo->obj))
              {
                // 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_&> (m->scope ()));

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

                // In a polymorphic hierarchy we have several tables and the
                // provided alias is used as a prefix together with the table
                // name to form the actual alias.
                //
                qname const& t (table_name (*c));

                if (vo->alias.empty ())
                  i->table = t;
                else
                  i->table = qname (vo->alias + "_" + t.uname ());
              }
              else
                i->table = vo->alias.empty ()
                  ? table_name (*vo->obj)
                  : qname (vo->alias);

              // Finally, resolve nested members if any.
              //
              for (; tt == CPP_DOT; tt = lex_.next (tl, &tn))
              {
                lex_.next (tl, &tn); // Get CPP_NAME.

                // Check that the outer member is composite and also
                // unwrap it while at it.
                //
                class_* comp (composite_wrapper (utype (*m)));
                if (comp == 0)
                {
                  error (i->loc) << "data member '" << m->name () << "' " <<
                    "specified in db pragma column is not composite" << endl;
                  throw operation_failed ();
                }

                m = &comp->lookup<data_member> (tl, class_::include_hidden);
                i->member_path.push_back (m);
              }

              // If the expression is just this reference, then we have
              // a source member.
              //
              if (e.size () == 1)
                src_m = m;
            }
            catch (lookup::invalid_name const&)
            {
              error (i->loc) << "invalid name in db pragma column" << endl;
              throw operation_failed ();
            }
            catch (semantics::unresolved const& e)
            {
              if (e.type_mismatch)
                error (i->loc) << "name '" << e.name << "' in db pragma " <<
                  "column does not refer to a data member" << endl;
              else
                error (i->loc) << "unable to resolve data member '" <<
                  e.name << "' specified with db pragma column" << endl;

              throw operation_failed ();
            }
            catch (semantics::ambiguous const& e)
            {
              error (i->loc) << "data member name '" << e.first.name () <<
                "' specified with db pragma column 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 ();
            }
          }

          // Check that the source member is not transient or inverse. Also
          // check that the C++ types are the same (sans cvr-qualification
          // and wrapping) and issue a warning if they differ. In rare cases
          // where this is not a mistake, the user can use a phony expression
          // (e.g., "" + person:name) to disable the warning. Note that in
          // this case there will be no type pragma copying, which is probably
          // ok seeing that the C++ types are different.
          //
          //
          if (src_m != 0)
          {
            string reason;

            if (transient (*src_m))
              reason = "transient";
            else if (inverse (*src_m))
              reason = "inverse";

            if (!reason.empty ())
            {
              error (e.loc)
                << "object data member '" << src_m->name () << "' specified "
                << "in db pragma column is " << reason << endl;
              throw operation_failed ();
            }

            if (!member_resolver::check_types (utype (*src_m), utype (m)))
            {
              warn (e.loc)
                << "object data member '" << src_m->name () << "' specified "
                << "in db pragma column has a different type compared to the "
                << "view data member" << endl;

              info (src_m->file (), src_m->line (), src_m->column ())
                << "object data member is defined here" << endl;

              info (m.file (), m.line (), m.column ())
                << "view data member is defined here" << endl;
            }
          }
        }
        // This member has no column information. If we are generating our
        // own query, try to find a member with the same (or similar) name
        // in one of the associated objects.
        //
        else if (query_.kind == view_query::condition)
        {
          view_objects& objs (view_.get<view_objects> ("objects"));

          assoc_members exact_members, pub_members;
          member_resolver resolver (exact_members, pub_members, m);

          for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
          {
            if (i->kind == view_object::object)
              resolver.traverse (*i);
          }

          assoc_members& members (
            !exact_members.empty () ? exact_members : pub_members);

          // Issue diagnostics if we didn't find any or found more
          // than one.
          //
          if (members.empty ())
          {
            error (m.file (), m.line (), m.column ())
              << "unable to find a corresponding data member for '"
              << m.name () << "' in any of the associated objects" << endl;

            info (m.file (), m.line (), m.column ())
              << "use db pragma column to specify the corresponding data "
              << "member or column name" << endl;

            throw operation_failed ();
          }
          else if (members.size () > 1)
          {
            error (m.file (), m.line (), m.column ())
              << "corresponding data member for '" << m.name () << "' is "
              << "ambiguous" << endl;

            info (m.file (), m.line (), m.column ())
              << "candidates are:" << endl;

            for (assoc_members::const_iterator i (members.begin ());
                 i != members.end ();
                 ++i)
            {
              info (i->m->file (), i->m->line (), i->m->column ())
                << "  '" << i->m->name () << "' in object '"
                << i->vo->name () << "'" << endl;
            }

            info (m.file (), m.line (), m.column ())
              << "use db pragma column to resolve this ambiguity" << endl;

            throw operation_failed ();
          }

          // Synthesize the column expression for this member.
          //
          assoc_member const& am (members.back ());

          column_expr& e (m.set ("column-expr", column_expr ()));
          e.push_back (column_expr_part ());
          column_expr_part& ep (e.back ());

          ep.kind = column_expr_part::reference;


          // If this object is polymorphic, then figure out which of the
          // bases this member comes from and use the corresponding table.
          //
          using semantics::class_;

          if (class_* root = polymorphic (*am.vo->obj))
          {
            class_* c (&static_cast<class_&> (am.m->scope ()));

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

            // In a polymorphic hierarchy we have several tables and the
            // provided alias is used as a prefix together with the table
            // name to form the actual alias.
            //
            qname const& t (table_name (*c));

            if (am.vo->alias.empty ())
              ep.table = t;
            else
              ep.table = qname (am.vo->alias + "_" + t.uname ());
          }
          else
            ep.table = am.vo->alias.empty ()
              ? table_name (*am.vo->obj)
              : qname (am.vo->alias);

          ep.member_path.push_back (am.m);

          src_m = am.m;
        }

        // If we have the source member and don't have the type pragma of
        // our own, but the source member does, then copy the columnt type
        // over. In case the source member is a pointer, also check the id
        // member.
        //
        if (src_m != 0 && !m.count ("type"))
        {
          if (src_m->count ("type"))
            m.set ("column-type", src_m->get<string> ("column-type"));
          else if (semantics::class_* c = object_pointer (utype (*src_m)))
          {
            semantics::data_member& id (*id_member (*c));

            if (id.count ("type"))
              m.set ("column-type", id.get<string> ("column-type"));
          }
        }

        // Check the return statements above if you add any extra logic
        // here.
      }

      struct member_resolver: traversal::class_
      {
        member_resolver (assoc_members& members,
                         assoc_members& pub_members,
                         semantics::data_member& m)
            : member_ (members, pub_members, m)
        {
          *this >> names_ >> member_;
          *this >> inherits_ >> *this;
        }

        void
        traverse (view_object& vo)
        {
          member_.vo_ = &vo;

          // First look for an exact match.
          //
          {
            member_.exact_ = true;
            member_.found_ = false;
            traverse (*vo.obj);
          }

          // If we didn't find an exact match, then look for a public
          // name match.
          //
          if (!member_.found_)
          {
            member_.exact_ = false;
            traverse (*vo.obj);
          }
        }

        virtual void
        traverse (type& c)
        {
          if (!object (c))
            return; // Ignore transient bases.

          names (c);

          // If we already found a match in one of the derived classes,
          // don't go into bases to get the standard "hiding" behavior.
          //
          if (!member_.found_)
            inherits (c);
        }

      public:
        static bool
        check_types (semantics::type& ot, semantics::type& vt)
        {
          using semantics::type;

          // Require that the types be the same sans the wrapping and
          // cvr-qualification. If the object member type is a pointer,
          // use the id type of the pointed-to object.
          //
          type* t1;

          if (semantics::class_* c = object_pointer (ot))
            t1 = &utype (*id_member (*c));
          else
            t1 = &ot;

          type* t2 (&vt);

          if (type* wt1 = context::wrapper (*t1))
            t1 = &utype (*wt1);

          if (type* wt2 = context::wrapper (*t2))
            t2 = &utype (*wt2);

          if (t1 != t2)
            return false;

          return true;
        }

      private:
        struct data_member: traversal::data_member
        {
          data_member (assoc_members& members,
                       assoc_members& pub_members,
                       semantics::data_member& m)
              : members_ (members),
                pub_members_ (pub_members),
                name_ (m.name ()),
                pub_name_ (context::current ().public_name (m)),
                type_ (utype (m))
          {
          }

          virtual void
          traverse (type& m)
          {
            if (exact_)
            {
              if (name_ == m.name () && check (m))
              {
                assoc_member am;
                am.m = &m;
                am.vo = vo_;
                members_.push_back (am);
                found_ = true;
              }
            }
            else
            {
              if (pub_name_ == context::current ().public_name (m) &&
                  check (m))
              {
                assoc_member am;
                am.m = &m;
                am.vo = vo_;
                pub_members_.push_back (am);
                found_ = true;
              }
            }
          }

          bool
          check (semantics::data_member& m)
          {
            // Make sure that the found node can possibly match.
            //
            if (context::transient (m) ||
                context::inverse (m) ||
                m.count ("polymorphic-ref"))
              return false;

            return check_types (utype (m), type_);
          }

          assoc_members& members_;
          assoc_members& pub_members_;

          string name_;
          string pub_name_;
          semantics::type& type_;

          view_object* vo_;
          bool exact_;
          bool found_;
        };

        traversal::names names_;
        data_member member_;
        traversal::inherits inherits_;
      };

    private:
      semantics::class_& view_;
      view_query& query_;
      view_alias_map& amap_;
      view_object_map& omap_;
      cxx_string_lexer lex_;
    };

    struct class_: traversal::class_, context
    {
      class_ ()
          : typedefs_ (true)
      {
        *this >> defines_ >> *this;
        *this >> typedefs_ >> *this;

        member_names_ >> member_;
      }

      virtual void
      traverse (type& c)
      {
        class_kind_type k (class_kind (c));

        if (k == class_other)
          return;

        names (c); // Process nested classes.
        names (c, member_names_);

        if (k == class_object)
          traverse_object (c);
        else if (k == class_view)
          traverse_view (c);
      }

      //
      // Object.
      //

      virtual void
      traverse_object (type& c)
      {
        // Remove the bulk pragma if this database doesn't support bulk
        // operations.
        //
        if (c.count ("bulk") && !generate_bulk)
          c.remove ("bulk");

        // Process indexes. Here we need to do two things: resolve member
        // names to member paths and assign names to unnamed indexes. We
        // are also going to handle the special container indexes.
        //
        indexes& ins (c.count ("index")
                      ? c.get<indexes> ("index")
                      : c.set ("index", indexes ()));

        for (indexes::iterator i (ins.begin ()); i != ins.end ();)
        {
          index& in (*i);

          // This should never happen since a db index pragma without
          // the member specifier will be treated as a member pragma.
          //
          assert (!in.members.empty ());

          // First resolve member names.
          //
          string tl;
          cpp_ttype tt (CPP_EOF);

          index::members_type::iterator j (in.members.begin ());
          for (; j != in.members.end (); ++j)
          {
            index::member& im (*j);

            if (!im.path.empty ())
              continue; // Already resolved.

            try
            {
              using semantics::data_member;

              // The name was already verified to be syntactically correct so
              // we don't need to do any extra error checking in this area.
              //
              lex_.start (im.name);
              tt = lex_.next (tl);

              data_member& m (
                c.lookup<data_member> (tl, type::include_hidden));

              im.path.push_back (&m);
              tt = lex_.next (tl);

              if (container (m))
                break;

              // Resolve nested members if any.
              //
              for (; tt == CPP_DOT; tt = lex_.next (tl))
              {
                lex_.next (tl); // Get CPP_NAME.

                data_member& om (*im.path.back ());

                // Check that the outer member is composite and also
                // unwrap it while at it.
                //
                semantics::class_* comp (composite_wrapper (utype (om)));
                if (comp == 0)
                {
                  error (im.loc) << "data member '" << om.name () << "' " <<
                    "specified in db pragma member is not composite" << endl;
                  throw operation_failed ();
                }

                data_member& nm (
                  comp->lookup<data_member> (tl, type::include_hidden));

                im.path.push_back (&nm);

                if (container (nm))
                {
                  tt = lex_.next (tl); // Get CPP_DOT.
                  break; // Only breaks out of the inner loop.
                }
              }

              if (container (*im.path.back ()))
                break;
            }
            catch (semantics::unresolved const& e)
            {
              if (e.type_mismatch)
                error (im.loc) << "name '" << e.name << "' in db pragma " <<
                  "member does not refer to a data member" << endl;
              else
                error (im.loc) << "unable to resolve data member '" <<
                  e.name << "' specified with db pragma member" << endl;

              throw operation_failed ();
            }
            catch (semantics::ambiguous const& e)
            {
              error (im.loc) << "data member name '" << e.first.name () <<
                "' specified with db pragma member 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 ();
            }
          }

          // Add the table prefix if this database has global index names.
          //
          if (!in.name.empty () && global_index)
            in.name = table_name_prefix (class_scope (c)) + in.name;

          // Handle container indexes.
          //
          if (j != in.members.end ())
          {
            // Do some sanity checks.
            //
            if (in.members.size () != 1)
            {
              error (in.loc) << "multiple data members specified for a "
                             << "container index" << endl;
              throw operation_failed ();
            }

            if (tt != CPP_DOT || lex_.next (tl) != CPP_NAME ||
                (tl != "id" && tl != "index"))
            {
              error (j->loc) << ".id or .index special member expected in a "
                             << "container index" << endl;
              throw operation_failed ();
            }

            string n (tl);

            if (lex_.next (tl) != CPP_EOF)
            {
              error (j->loc) << "unexpected text after ." << n << " in "
                             << "db pragma member" << endl;
              throw operation_failed ();
            }

            // Move this index to the container member.
            //
            j->path.back ()->set (n + "-index", *i);
            i = ins.erase (i);
            continue;
          }

          // Now assign the name if the index is unnamed. We have to
          // add table name as a prefix here since there is not way
          // to distinguish between user-assigned and auto-derived
          // names in the model.
          //
          if (in.name.empty ())
          {
            // Make sure there is only one member.
            //
            if (in.members.size () > 1)
            {
              error (in.loc) << "unnamed index with more than one data "
                             << "member" << endl;
              throw operation_failed ();
            }

            // Generally, we want the index name to be based on the column
            // name. This is straightforward for single-column members. In
            // case of a composite member, we will need to use the column
            // prefix which is based on the data member name, unless
            // overridden by the user. In the latter case the prefix can
            // be empty, in which case we will just fall back on the
            // member's public name.
            //
            string n (column_prefix (in.members.front ().path, true).prefix);

            if (n.empty ())
              n = public_name_db (*in.members.front ().path.back ());
            else if (n[n.size () - 1] == '_')
              n.resize (n.size () - 1); // Remove trailing underscore.

            in.name = index_name (table_name (c), n);
          }

          ++i;
        }
      }

      //
      // View.
      //

      struct relationship
      {
        semantics::data_member* member;
        string name;
        view_object* pointer;
        view_object* pointee;
      };

      typedef vector<relationship> relationships;

      virtual void
      traverse_view (type& c)
      {
        bool has_q (c.count ("query"));
        bool has_o (c.count ("objects"));

        // Determine the kind of query template we've got.
        //
        view_query& vq (has_q
                        ? c.get<view_query> ("query")
                        : c.set ("query", view_query ()));
        if (has_q)
        {
          if (!vq.literal.empty ())
          {
            string q (upcase (vq.literal));

            if (q.compare (0, 7, "SELECT ") == 0)
              vq.kind = view_query::complete_select;
            else if (q.compare (0, 5, "EXEC ") == 0 ||
                     q.compare (0, 5, "CALL ") == 0 ||
                     q.compare (0, 8, "EXECUTE ") == 0)
              vq.kind = view_query::complete_execute;
            else
              vq.kind = view_query::condition;
          }
          else if (!vq.expr.empty ())
          {
            // If the first token in the expression is a string and
            // it starts with "SELECT " or is equal to "SELECT" or
            // one of the stored procedure call keywords, then we
            // have a complete query.
            //
            if (vq.expr.front ().type == CPP_STRING)
            {
              string q (upcase (vq.expr.front ().literal));

              if (q.compare (0, 7, "SELECT ") == 0 || q == "SELECT")
                vq.kind = view_query::complete_select;
              else if (q.compare (0, 5, "EXEC ") == 0 || q == "EXEC" ||
                       q.compare (0, 5, "CALL ") == 0 || q == "CALL" ||
                       q.compare (0, 8, "EXECUTE ") == 0 || q == "EXECUTE")
                vq.kind = view_query::complete_execute;
              else
                vq.kind = view_query::condition;
            }
            else
              vq.kind = view_query::condition;
          }
          else
            vq.kind = (vq.distinct || vq.for_update)
              ? view_query::condition // The query(distinct) case.
              : view_query::runtime;
        }
        else
          vq.kind = has_o ? view_query::condition : view_query::runtime;

        if ((vq.distinct || vq.for_update) && vq.kind != view_query::condition)
        {
          error (vq.loc)
            << "result modifier specified for "
            << (vq.kind == view_query::runtime ? "runtime" : "native")
            << " query" << endl;

          throw operation_failed ();
        }

        // We cannot have an incomplete query if there are not objects
        // to derive the rest from.
        //
        if (vq.kind == view_query::condition && !has_o)
        {
          error (c.file (), c.line (), c.column ())
            << "view '" << class_fq_name (c) << "' has an incomplete query "
            << "template and no associated objects" << endl;

          info (c.file (), c.line (), c.column ())
            << "use db pragma query to provide a complete query template"
            << endl;

          info (c.file (), c.line (), c.column ())
            << "or use db pragma object to associate one or more objects "
            << "with the view"
            << endl;

          throw operation_failed ();
        }

        // Process join conditions.
        //
        if (has_o)
        {
          view_objects& objs (c.get<view_objects> ("objects"));

          for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
          {
            if (i == objs.begin () && i->join != view_object::left)
            {
              error (i->loc)
                << "no join type can be specified for the first associated "
                << (i->kind == view_object::object ? "object" : "table")
                << endl;
              throw operation_failed ();
            }

            if (i->kind != view_object::object)
            {
              // Make sure we have join conditions for tables unless it
              // is the first entry.
              //
              if (i != objs.begin () && i->cond.empty ())
              {
                error (i->loc)
                  << "missing join condition in db pragma table" << endl;

                throw operation_failed ();
              }

              continue;
            }

            // If we have to generate the query and there was no JOIN
            // condition specified by the user, try to come up with one
            // automatically based on object relationships. CROSS JOIN
            // has no condition.
            //
            if (vq.kind == view_query::condition &&
                i->cond.empty () &&
                i != objs.begin () &&
                i->join != view_object::cross)
            {
              relationships rs;

              // Check objects specified prior to this one for any
              // relationships. We don't examine objects that were
              // specified after this one because that would require
              // rearranging the JOIN order.
              //
              for (view_objects::iterator j (objs.begin ()); j != i; ++j)
              {
                if (j->kind != view_object::object)
                  continue; // Skip tables.

                // First see if any of the objects that were specified
                // prior to this object point to it.
                //
                {
                  relationship_resolver r (rs, *i, true);
                  r.traverse (*j);
                }

                // Now see if this object points to any of the objects
                // specified prior to it.
                //
                {
                  relationship_resolver r (rs, *j, false);
                  r.traverse (*i);
                }
              }

              // Issue diagnostics if we didn't find any or found more
              // than one.
              //
              if (rs.empty ())
              {
                error (i->loc)
                  << "unable to find an object relationship involving "
                  << "object '" << i->name () << "' and any of the previously "
                  << "associated objects" << endl;

                info (i->loc)
                  << "use the join condition clause in db pragma object "
                  << "to specify a custom join condition" << endl;

                throw operation_failed ();
              }
              else if (rs.size () > 1)
              {
                error (i->loc)
                  << "object relationship for object '" << i->name () <<  "' "
                  << "is ambiguous" << endl;

                info (i->loc)
                  << "candidates are:" << endl;

                for (relationships::const_iterator j (rs.begin ());
                     j != rs.end ();
                     ++j)
                {
                  semantics::data_member& m (*j->member);

                  info (m.file (), m.line (), m.column ())
                    << "  '" << j->name << "' "
                    << "in object '" << j->pointer->name () << "' "
                    << "pointing to '" << j->pointee->name () << "'"
                    << endl;
                }

                info (i->loc)
                  << "use the join condition clause in db pragma object "
                  << "to resolve this ambiguity" << endl;

                throw operation_failed ();
              }

              // Synthesize the condition.
              //
              relationship const& r (rs.back ());

              string name (r.pointer->alias.empty ()
                           ? class_fq_name (*r.pointer->obj)
                           : r.pointer->alias);
              name += "::";
              name += r.name;

              lex_.start (name);

              string t;
              for (cpp_ttype tt (lex_.next (t));
                   tt != CPP_EOF;
                   tt = lex_.next (t))
              {
                i->cond.push_back (cxx_token (lex_.location (), tt, t));
              }
            }
          }
        }

        // Handle forced versioning. When versioning is forced, ignore
        // it for native views.
        //
        if (force_versioned && vq.kind == view_query::condition)
          c.set ("versioned", true);

        // Handle data members.
        //
        {
          view_data_member t (c);
          traversal::names n (t);
          names (c, n);
        }
      }

      struct relationship_resolver: object_members_base
      {
        relationship_resolver (relationships& rs,
                               view_object& pointee,
                               bool forward)
            // Look in polymorphic bases only for previously-associated
            // objects since backward pointers from bases will result in
            // the pathological case (we will have to join the base table
            // first, which means we will get both bases and derived objects
            // instead of just derived).
            //
            : object_members_base (false, false, true, forward),
              relationships_ (rs),
              // Ignore self-references if we are looking for backward
              // pointers since they were already added to the list in
              // the previous pass.
              //
              self_pointer_ (forward),
              pointer_ (0),
              pointee_ (pointee)
        {
        }

        void
        traverse (view_object& pointer)
        {
          pointer_ = &pointer;
          object_members_base::traverse (*pointer.obj);
        }

        virtual void
        traverse_pointer (semantics::data_member& m, semantics::class_& c)
        {
          // Ignore polymorphic id references.
          //
          if (m.count ("polymorphic-ref"))
            return;

          // Ignore inverse sides of the same relationship to avoid
          // phony conflicts caused by the direct side that will end
          // up in the relationship list as well. Unless the inverse
          // member is in the polymorphic base in which case we will
          // miss it since we don't examine inside poly bases on the
          // backwards scan (see above).
          //
          if (semantics::data_member* im = inverse (m))
          {
            if (&im->scope () == &c) // Direct member.
              return;
          }

          // Ignore self-pointers if requested.
          //
          if (!self_pointer_ && pointer_->obj == &c)
            return;

          if (pointee_.obj == &c)
          {
            relationships_.push_back (relationship ());
            relationships_.back ().member = &m;
            relationships_.back ().name = member_prefix_ + m.name ();
            relationships_.back ().pointer = pointer_;
            relationships_.back ().pointee = &pointee_;
          }
        }

        virtual void
        traverse_container (semantics::data_member& m, semantics::type& t)
        {
          if (semantics::class_* c =
              object_pointer (context::container_vt (t)))
          {
            if (inverse (m, "value"))
              return;

            // Ignore self-pointers if requested.
            //
            if (!self_pointer_ && pointer_->obj == c)
              return;

            if (pointee_.obj == c)
            {
              relationships_.push_back (relationship ());
              relationships_.back ().member = &m;
              relationships_.back ().name = member_prefix_ + m.name ();
              relationships_.back ().pointer = pointer_;
              relationships_.back ().pointee = &pointee_;
            }
          }
        }

      private:
        relationships& relationships_;
        bool self_pointer_;
        view_object* pointer_;
        view_object& pointee_;
      };

    private:
      cxx_string_lexer lex_;

      traversal::defines defines_;
      typedefs typedefs_;

      data_member member_;
      traversal::names member_names_;
    };
  }

  void
  process ()
  {
    context ctx;

    traversal::unit unit;
    traversal::defines unit_defines;
    typedefs unit_typedefs (true);
    traversal::namespace_ ns;
    class_ c;

    unit >> unit_defines >> ns;
    unit_defines >> c;
    unit >> unit_typedefs >> c;

    traversal::defines ns_defines;
    typedefs ns_typedefs (true);

    ns >> ns_defines >> ns;
    ns_defines >> c;
    ns >> ns_typedefs >> c;

    unit.dispatch (ctx.unit);
  }
}
