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

#include <odb/gcc.hxx>

#include <cctype> // std::isalnum
#include <limits>
#include <vector>
#include <sstream>

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

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

using namespace std;
using namespace cutl;

using container::any;

virt_declarations virt_declarations_;
loc_pragmas loc_pragmas_;
decl_pragmas decl_pragmas_;
ns_loc_pragmas ns_loc_pragmas_;
pragma_name_set simple_value_pragmas_;

template <typename X>
void
accumulate (compiler::context& ctx, string const& k, any const& v, location_t)
{
  // Empty values are used to indicate that this pragma must be ignored.
  //
  if (v.empty ())
    return;

  typedef vector<X> container;

  container& c (ctx.count (k)
                ? ctx.get<container> (k)
                : ctx.set (k, container ()));

  c.push_back (v.value<X> ());
}

// Parse a qualified string. It can be in one of the following formats:
//
// "foo.bar.baz"     (can have empty leading component, e.g., ".bar.baz")
// "foo"."bar"."baz" (can have empty leading component, e.g., ""."bar"."baz")
// ."foo.bar"        (used to specify unqualifed names with periods)
//
// Empty leading components means fully qualified (similar to ::foo in C++).
//
static bool
parse_qname (cxx_lexer& l,
             cpp_ttype& tt,
             string& tl,
             tree& tn,
             string const& p, // Pragma name for diagnostic.
             qname& name,
             bool* expr = 0,       // If specified, detect an expression
             string* expr_str = 0) // and store it here.
{
  assert (tt == CPP_STRING || tt == CPP_DOT);

  // Handle the special case of unqualified name which can contain periods.
  //
  if (tt == CPP_DOT)
  {
    tt = l.next (tl, &tn);

    if (tt != CPP_STRING)
    {
      error (l) << "name expected after '.' in db pragma " << p << endl;
      return false;
    }

    name = tl;
    tt = l.next (tl, &tn);
    return true;
  }

  name.clear ();
  string str (tl);

  // See what comes after the string.
  //
  tt = l.next (tl, &tn);

  if (tt == CPP_DOT)
  {
    name.append (str);

    for (; tt == CPP_DOT; tt = l.next (tl, &tn))
    {
      tt = l.next (tl, &tn);

      if (tt != CPP_STRING)
      {
        error (l) << "name expected after '.' in db pragma " << p << endl;
        return false;
      }

      name.append (tl);
    }

    return true;
  }

  if (expr != 0 && tt == CPP_PLUS)
  {
    *expr = true;
    *expr_str = str;
    return true;
  }

  // Scan the string looking for '.' as well as for non-identifier
  // characters if we need to detect expressions.
  //
  string::size_type prev (string::npos);

  for (size_t i (0); i < str.size (); ++i)
  {
    char c (str[i]);

    if (c == '.')
    {
      if (prev == string::npos)
        name.append (string (str, 0, i));
      else
        name.append (string (str, prev + 1, i - prev - 1));

      prev  = i;
    }
    else if (expr != 0 && !(isalnum (c) || c == '_'))
    {
      *expr = true;
      *expr_str = str;
      return true;
    }
  }

  if (prev == string::npos)
    name.append (str);
  else
    name.append (string (str, prev + 1, string::npos));

  return true;
}

static bool
parse_expression (cxx_lexer& l,
                  cpp_ttype& tt,
                  string& tl,
                  tree& tn,
                  cxx_tokens& ts,
                  string const& prag)
{
  // Keep reading tokens until we see a matching ')' while keeping track
  // of their balance.
  //
  size_t balance (0);

  for (; tt != CPP_EOF; tt = l.next (tl, &tn))
  {
    bool done (false);
    cxx_token ct (l.location (), tt);

    switch (tt)
    {
    case CPP_OPEN_PAREN:
      {
        balance++;
        break;
      }
    case CPP_CLOSE_PAREN:
      {
        if (balance == 0)
          done = true;
        else
          balance--;
        break;
      }
    case CPP_STRING:
      {
        ct.literal = tl;
        break;
      }
    case CPP_NAME:
  //case CPP_KEYWORD: see default:
      {
        ct.literal = tl;
        break;
      }
    case CPP_NUMBER:
      {
        switch (TREE_CODE (tn))
        {
        case INTEGER_CST:
          {
            tree type (TREE_TYPE (tn));

            HOST_WIDE_INT hwl (TREE_INT_CST_LOW (tn));
            HOST_WIDE_INT hwh (TREE_INT_CST_HIGH (tn));

            unsigned long long l (hwl);
            unsigned long long h (hwh);
            unsigned short width (HOST_BITS_PER_WIDE_INT);

            unsigned long long v ((h << width) + l);

            ostringstream os;
            os << v;

            if (type == long_long_integer_type_node)
              os << "LL";
            else if (type == long_long_unsigned_type_node)
              os << "ULL";
            else if (type == long_integer_type_node)
              os << "L";
            else if (type == long_unsigned_type_node)
              os << "UL";
            else if (
              TYPE_UNSIGNED (type) &&
              TYPE_PRECISION (type) >= TYPE_PRECISION (integer_type_node))
              os << "U";

            ct.literal = os.str ();
            break;
          }
        case REAL_CST:
          {
            tree type (TREE_TYPE (tn));
            REAL_VALUE_TYPE val (TREE_REAL_CST (tn));

            // This is the best we can do. val cannot be INF or NaN.
            //
            char tmp[256];
            real_to_decimal (tmp, &val, sizeof (tmp), 0, true);
            istringstream is (tmp);
            ostringstream os;

            if (type == float_type_node)
            {
              float f;
              is >> f;
              os << f << 'F';
            }
            else
            {
              double d;
              is >> d;
              os << d;
            }

            ct.literal = os.str ();
            break;
          }
        default:
          {
            error (l) << "unexpected numeric constant in db pragma "
                      << prag << endl;
            return false;
          }
        }

        break;
      }
    default:
      {
        // CPP_KEYWORD is not in the cpp_ttype enumeration.
        //
        if (tt == CPP_KEYWORD)
          ct.literal = tl;

        break;
      }
    }

    if (done)
      break;

    // We don't store the tree node in ct since we converted numbers to
    // string literals.
    //
    ts.push_back (ct);
  }

  return true;
}


static string
parse_scoped_name (cxx_lexer& l,
                   cpp_ttype& tt,
                   string& tl,
                   tree& tn,
                   string const& prag)
{
  try
  {
    return lookup::parse_scoped_name (l, tt, tl, tn);
  }
  catch (lookup::invalid_name const&)
  {
    error (l) << "invalid name in db pragma " << prag << endl;
    return "";
  }
}

static tree
resolve_scoped_name (cxx_lexer& l,
                     cpp_ttype& tt,
                     string& tl,
                     tree& tn,
                     tree start_scope,
                     string& name,
                     bool is_type,
                     string const& prag,
                     bool trailing_scope = false,
                     cpp_ttype* prev_tt = 0)
{
  try
  {
    cpp_ttype ptt; // Not used.
    tree r (
      lookup::resolve_scoped_name (
        l, tt, tl, tn, ptt, start_scope, name, is_type, trailing_scope));

    if (prev_tt != 0)
      *prev_tt = ptt;

    return r;
  }
  catch (lookup::invalid_name const&)
  {
    error (l) << "invalid name in db pragma " << prag << endl;
    return 0;
  }
  catch (lookup::unable_to_resolve const& e)
  {
    if (e.last ())
      error (l) << "unable to resolve " << (is_type ? "type " : "") << "name "
                << "'" << e.name () << "' in db pragma " << prag << endl;
    else
      error (l) << "unable to resolve name '" << e.name () << "' in db pragma "
                << prag << endl;

    return 0;
  }
}

static bool
check_spec_decl_type (declaration const& d,
                      string const& name,
                      string const& p,
                      location_t l)
{
  int tc (d.tree_code ());
  bool type (TREE_CODE_CLASS (tc) == tcc_type);

  if (p == "no_id")
  {
    // No_id can be used on objects only.
    //
    if (tc != RECORD_TYPE)
    {
      error (l) << "name '" << name << "' in db pragma " << p << " does "
                << "not refer to a data member or class" << endl;
      return false;
    }
  }
  else if (p == "id"        ||
           p == "auto"      ||
           p == "column"    ||
           p == "inverse"   ||
           p == "version"   ||
           p == "index"     ||
           p == "unique"    ||
           p == "get"       ||
           p == "set"       ||
           p == "access")
  {
    if (tc != FIELD_DECL)
    {
      error (l) << "name '" << name << "' in db pragma " << p << " does "
                << "not refer to a data member" << endl;
      return false;
    }
  }
  else if (p == "transient")
  {
    // Transient can be used for both data members and classes (object,
    // view, or composite value).
    //
    if (tc != FIELD_DECL && tc != RECORD_TYPE)
    {
      error (l) << "name '" << name << "' in db pragma " << p << " does "
                << "not refer to a data member or class" << endl;
      return false;
    }
  }
  else if (p == "readonly")
  {
    // Readonly can be used for both data members and classes (object or
    // composite value).
    //
    if (tc != FIELD_DECL && tc != RECORD_TYPE)
    {
      error (l) << "name '" << name << "' in db pragma " << p << " does "
                << "not refer to a data member or class" << endl;
      return false;
    }
  }
  else if (p == "abstract" ||
           p == "callback" ||
           p == "query" ||
           p == "object" ||
           p == "optimistic" ||
           p == "polymorphic" ||
           p == "definition")
  {
    if (tc != RECORD_TYPE)
    {
      error (l) << "name '" << name << "' in db pragma " << p << " does "
                << "not refer to a class" << endl;
      return false;
    }
  }
  else if (p == "pointer")
  {
    // Table can be used for namespaces and classes (object or view).
    //
    if (tc != NAMESPACE_DECL && tc != RECORD_TYPE)
    {
      error (l) << "name '" << name << "' in db pragma " << p << " does "
                << "not refer to a class" << endl;
      return false;
    }
  }
  else if (p == "table")
  {
    // Table can be used for namespaces, members (container), and types
    // (container, object, or view).
    //
    if (tc != NAMESPACE_DECL && tc != FIELD_DECL && !type)
    {
      error (l) << "name '" << name << "' in db pragma " << p << " does "
                << "not refer to a namespace, type, or data member" << endl;
      return false;
    }
  }
  else if (p == "session")
  {
    // Session can be used only for namespaces and objects.
    //
    if (tc != NAMESPACE_DECL && tc != RECORD_TYPE)
    {
      error (l) << "name '" << name << "' in db pragma " << p << " does "
                << "not refer to a namespace or class" << endl;
      return false;
    }
  }
  else if (p == "schema")
  {
    // For now schema can be used only for namespaces and objects.
    //
    if (tc != NAMESPACE_DECL && tc != RECORD_TYPE)
    {
      error (l) << "name '" << name << "' in db pragma " << p << " does "
                << "not refer to a namespace or class" << endl;
      return false;
    }
  }
  else if (p == "type" ||
           p == "id_type" ||
           p == "value_type" ||
           p == "index_type" ||
           p == "key_type")
  {
    // Type can be used for both members and types.
    //
    if (tc != FIELD_DECL && !type)
    {
      error (l) << "name '" << name << "' in db pragma " << p << " does "
                << "not refer to a type or data member" << endl;
      return false;
    }
  }
  else if (p == "default")
  {
    // Default can be used for both members and types.
    //
    if (tc != FIELD_DECL && !type)
    {
      error (l) << "name '" << name << "' in db pragma " << p << " does "
                << "not refer to a type or data member" << endl;
      return false;
    }
  }
  else if (p == "value_column" ||
           p == "index_column" ||
           p == "key_column" ||
           p == "id_column")
  {
    // Container columns can be used for both members (container) and
    // types (container).
    //
    if (tc != FIELD_DECL && !type)
    {
      error (l) << "name '" << name << "' in db pragma " << p << " does "
                << "not refer to a type or data member" << endl;
      return false;
    }
  }
  else if (p == "options" ||
           p == "value_options" ||
           p == "index_options" ||
           p == "key_options" ||
           p == "id_options")
  {
    // Options can be used for both members and types.
    //
    if (tc != FIELD_DECL && !type)
    {
      error (l) << "name '" << name << "' in db pragma " << p << " does "
                << "not refer to a type or data member" << endl;
      return false;
    }
  }
  else if (p == "null" ||
           p == "not_null" ||
           p == "value_null" ||
           p == "value_not_null")
  {
    // Null pragmas can be used for both members and types (values,
    // containers, and pointers).
    //
    if (tc != FIELD_DECL && !type)
    {
      error (l) << "name '" << name << "' in db pragma " << p << " does "
                << "not refer to a type or data member" << endl;
      return false;
    }
  }
  else if (p == "unordered")
  {
    // Unordered can be used for both members (container) and
    // types (container).
    //
    if (tc != FIELD_DECL && !type)
    {
      error (l) << "name '" << name << "' in db pragma " << p << " does "
                << "not refer to a type or data member" << endl;
      return false;
    }
  }
  else if (p == "virtual")
  {
    // Virtual is specified for a member.
    //
    if (tc != FIELD_DECL)
    {
      error (l) << "name '" << name << "' in db pragma " << p << " does "
                << "not refer to a data member" << endl;
      return false;
    }
  }
  else
  {
    error (l) << "unknown db pragma " << p << endl;
    return false;
  }

  return true;
}

static void
add_pragma (pragma const& prag, declaration const& decl, bool ns)
{
  if (decl)
    decl_pragmas_[decl].insert (prag);
  else
  {
    tree scope (current_scope ());

    if (!ns)
    {
      if (!CLASS_TYPE_P (scope))
        scope = global_namespace;

      loc_pragmas_[scope].push_back (prag);
    }
    else
      ns_loc_pragmas_.push_back (ns_loc_pragma (scope, prag));
  }
}

static void
handle_pragma (cxx_lexer& l,
               string const& p,
               string const& qualifier,
               any& qualifier_value,
               declaration const& decl,
               string const& decl_name,
               bool ns) // True if this is a position namespace pragma.
{
  cpp_ttype tt;
  string tl;
  tree tn;

  string name (p);                           // Pragma name.
  any val;                                   // Pragma value.
  pragma::add_func adder (0);                // Custom context adder.
  location_t loc (l.location ());            // Pragma location.

  if (qualifier == "map")
  {
    // type("<regex>")
    // as("<subst>")
    // to("<subst>")
    // from("<subst>")
    //

    if (p != "type" &&
        p != "as"   &&
        p != "to"   &&
        p != "from")
    {
      error (l) << "unknown db pragma " << p << endl;
      return;
    }

    using relational::custom_db_type;

    // Make sure we've got the correct declaration type.
    //
    assert (decl == global_namespace);
    custom_db_type& ct (qualifier_value.value<custom_db_type> ());

    if (l.next (tl, &tn) != CPP_OPEN_PAREN)
    {
      error (l) << "'(' expected after db pragma " << p << endl;
      return;
    }

    tt = l.next (tl, &tn);

    if (p == "type")
    {
      if (tt != CPP_STRING)
      {
        error (l) << "type name regex expected in db pragma " << p << endl;
        return;
      }

      try
      {
        // Make it case-insensitive.
        //
        ct.type.assign (tl, true);
      }
      catch (regex_format const& e)
      {
        error (l) << "invalid regex: '" << e.regex () << "' in db pragma "
                  << p << ": " << e.description () << endl;
        return;
      }
    }
    else if (p == "as")
    {
      if (tt != CPP_STRING)
      {
        error (l) << "type name expected in db pragma " << p << endl;
        return;
      }

      ct.as = tl;
    }
    else if (p == "to")
    {
      if (tt != CPP_STRING)
      {
        error (l) << "expression expected in db pragma " << p << endl;
        return;
      }

      ct.to = tl;
    }
    else if (p == "from")
    {
      if (tt != CPP_STRING)
      {
        error (l) << "expression expected in db pragma " << p << endl;
        return;
      }

      ct.from = tl;
    }

    if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
    {
      error (l) << "')' expected at the end of db pragma " << p << endl;
      return;
    }

    name.clear (); // We don't need to add anything for this pragma.
    tt = l.next (tl, &tn);
  }
  else if (qualifier == "index")
  {
    // unique
    // type("<type>")
    // method("<method>")
    // options("<options>")
    // member(<name>[, "<options>"])
    // members(<name>[, <name>...])
    //

    if (p != "unique"  &&
        p != "type"    &&
        p != "method"  &&
        p != "options" &&
        p != "member"  &&
        p != "members")
    {
      error (l) << "unknown db pragma " << p << endl;
      return;
    }

    using relational::index;
    index& in (qualifier_value.value<index> ());

    if (p == "unique")
      in.type = "UNIQUE";
    else
    {
      if (l.next (tl, &tn) != CPP_OPEN_PAREN)
      {
        error (l) << "'(' expected after db pragma " << p << endl;
        return;
      }

      tt = l.next (tl, &tn);

      if (p == "type")
      {
        if (tt != CPP_STRING)
        {
          error (l) << "index type expected in db pragma " << p << endl;
          return;
        }

        in.type = tl;
        tt = l.next (tl, &tn);
      }
      else if (p == "method")
      {
        if (tt != CPP_STRING)
        {
          error (l) << "index method expected in db pragma " << p << endl;
          return;
        }

        in.method = tl;
        tt = l.next (tl, &tn);
      }
      else if (p == "options")
      {
        if (tt != CPP_STRING)
        {
          error (l) << "index options expected in db pragma " << p << endl;
          return;
        }

        in.options = tl;
        tt = l.next (tl, &tn);
      }
      else if (p == "member")
      {
        if (tt != CPP_NAME)
        {
          error (l) << "data member name expected in db pragma " << p << endl;
          return;
        }

        index::member m;
        m.loc = loc;
        m.name = tl;

        tt = l.next (tl, &tn);

        // Parse nested members if any.
        //
        for (; tt == CPP_DOT; tt = l.next (tl, &tn))
        {
          if (l.next (tl, &tn) != CPP_NAME)
          {
            error (l) << "name expected after '.' in db pragma " << p << endl;
            return;
          }

          m.name += '.';
          m.name += tl;
        }

        // Parse member options, if any.
        //
        if (tt == CPP_COMMA)
        {
          if (l.next (tl, &tn) != CPP_STRING)
          {
            error (l) << "index member options expected in db pragma " << p
                      << endl;
            return;
          }

          m.options = tl;
          tt = l.next (tl, &tn);
        }

        in.members.push_back (m);
      }
      else if (p == "members")
      {
        for (;;)
        {
          if (tt != CPP_NAME)
          {
            error (l) << "data member name expected in db pragma " << p
                      << endl;
            return;
          }

          index::member m;
          m.loc = l.location ();
          m.name = tl;

          tt = l.next (tl, &tn);

          // Parse nested members if any.
          //
          for (; tt == CPP_DOT; tt = l.next (tl, &tn))
          {
            if (l.next (tl, &tn) != CPP_NAME)
            {
              error (l) << "name expected after '.' in db pragma " << p
                        << endl;
              return;
            }

            m.name += '.';
            m.name += tl;
          }

          in.members.push_back (m);

          if (tt == CPP_COMMA)
            tt = l.next (tl, &tn);
          else
            break;
        }
      }

      if (tt != CPP_CLOSE_PAREN)
      {
        error (l) << "')' expected at the end of db pragma " << p << endl;
        return;
      }
    }

    name.clear (); // We don't need to add anything for this pragma.
    tt = l.next (tl, &tn);
  }
  else if (p == "index" ||
           p == "unique")
  {
    // index
    // unique
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    tt = l.next (tl, &tn);
  }
  else if (p == "get" ||
           p == "set" ||
           p == "access")
  {
    // get(name|expr)
    // set(name|expr)
    // access(name|expr)
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    if (l.next (tl, &tn) != CPP_OPEN_PAREN)
    {
      error (l) << "'(' expected after db pragma " << p << endl;
      return;
    }

    tt = l.next (tl, &tn);

    val = member_access (loc, false);
    if (!parse_expression (l, tt, tl, tn, val.value<member_access> ().expr, p))
      return; // Diagnostics has already been issued.

    if (tt != CPP_CLOSE_PAREN)
    {
      error (l) << "')' expected at the end of db pragma " << p << endl;
      return;
    }

    // Convert access to the get/set pair.
    //
    if (p == "access")
    {
      add_pragma (
        pragma (p, "get", val, loc, &check_spec_decl_type, 0), decl, ns);
      name = "set";
    }

    tt = l.next (tl, &tn);
  }
  else if (p == "table")
  {
    // table (<name>)
    // table (<name> [= "<alias>"] [: "<cond>"]  (view only)
    //
    // <name> := "name" | "name.name" | "name"."name"
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    if (l.next (tl, &tn) != CPP_OPEN_PAREN)
    {
      error (l) << "'(' expected after db pragma " << p << endl;
      return;
    }

    tt = l.next (tl, &tn);

    if (tt != CPP_STRING && tt != CPP_DOT)
    {
      error (l) << "table name expected in db pragma " << p << endl;
      return;
    }

    // The table specifier is used for both objects and views. In case
    // of an object, the context values is just a string. In case of a
    // view, the context value is a view_object entry. The problem is
    // that at this stage we don't know whether we are dealing with an
    // object or a view. To resolve this in a somewhat hackish way, we
    // are going to create both a string and a view_object entry.
    //
    view_object vo;
    vo.kind = view_object::table;

    if (!parse_qname (l, tt, tl, tn, p, vo.tbl_name))
      return; // Diagnostics has already been issued.

    if (tt == CPP_EQ)
    {
      // We have an alias.
      //
      if (l.next (tl, &tn) != CPP_STRING)
      {
        error (l) << "table alias expected after '=' in db pragma " << p
                  << endl;
        return;
      }

      vo.alias = tl;
      tt = l.next (tl, &tn);
    }

    if (tt == CPP_COLON)
    {
      // We have a condition.

      tt = l.next (tl, &tn);

      if (!parse_expression (l, tt, tl, tn, vo.cond, p))
        return; // Diagnostics has already been issued.

      if (vo.cond.empty ())
      {
        error (l) << "join condition expected after ':' in db pragma " << p
                  << endl;
        return;
      }
    }

    if (tt != CPP_CLOSE_PAREN)
    {
      error (l) << "')' expected at the end of db pragma " << p << endl;
      return;
    }

    // Add the "table" pragma.
    //
    if (vo.alias.empty () && vo.cond.empty ())
      add_pragma (
        pragma (p, name, vo.tbl_name, loc, &check_spec_decl_type, 0),
        decl,
        ns);

    vo.scope = current_scope ();
    vo.loc = loc;
    val = vo;
    name = "objects";
    adder = &accumulate<view_object>;

    tt = l.next (tl, &tn);
  }
  else if (p == "session")
  {
    // session
    // session (true|false)
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    tt = l.next (tl, &tn);

    if (tt == CPP_OPEN_PAREN)
    {
      tt = l.next (tl, &tn);

      if (tt != CPP_KEYWORD || (tl != "true" && tl != "false"))
      {
        error (l) << "true or false expected in db pragma " << p << endl;
        return;
      }

      val = (tl == "true");

      tt = l.next (tl, &tn);
      if (tt != CPP_CLOSE_PAREN)
      {
        error (l) << "')' expected at the end of db pragma " << p << endl;
        return;
      }

      tt = l.next (tl, &tn);
    }
  }
  else if (p == "schema")
  {
    // schema (<name>)
    //
    // <name> := "name" | "name.name" | "name"."name"
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    if (l.next (tl, &tn) != CPP_OPEN_PAREN)
    {
      error (l) << "'(' expected after db pragma " << p << endl;
      return;
    }

    tt = l.next (tl, &tn);

    if (tt != CPP_STRING && tt != CPP_DOT)
    {
      error (l) << "table name expected in db pragma " << p << endl;
      return;
    }

    qname s;
    if (!parse_qname (l, tt, tl, tn, p, s))
      return; // Diagnostics has already been issued.

    if (tt != CPP_CLOSE_PAREN)
    {
      error (l) << "')' expected at the end of db pragma " << p << endl;
      return;
    }

    val = s;
    tt = l.next (tl, &tn);
  }
  else if (p == "pointer")
  {
    // pointer (qname)
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    if (l.next (tl, &tn) != CPP_OPEN_PAREN)
    {
      error (l) << "'(' expected after db pragma " << p << endl;
      return;
    }

    class_pointer cp;
    size_t pb (0);
    bool punc (false);

    for (tt = l.next (tl, &tn);
         tt != CPP_EOF && (tt != CPP_CLOSE_PAREN || pb != 0);
         tt = l.next (tl, &tn))
    {
      if (punc && tt > CPP_LAST_PUNCTUATOR)
        cp.name += ' ';

      punc = false;

      if (tt == CPP_OPEN_PAREN)
        pb++;
      else if (tt == CPP_CLOSE_PAREN)
        pb--;

      // @@ Need to handle literals, at least integer.
      //
      switch (tt)
      {
      case CPP_LESS:
        {
          cp.name += "< ";
          break;
        }
      case CPP_GREATER:
        {
          cp.name += " >";
          break;
        }
      case CPP_COMMA:
        {
          cp.name += ", ";
          break;
        }
      case CPP_NAME:
      // case CPP_KEYWORD: // see default:
        {
          cp.name += tl;
          punc = true;
          break;
        }
      default:
        {
          if (tt == CPP_KEYWORD)
          {
            cp.name += tl;
            punc = true;
          }
          else if (tt <= CPP_LAST_PUNCTUATOR)
            cp.name += cxx_lexer::token_spelling[tt];
          else
          {
            error (l) << "unexpected token '" << cxx_lexer::token_spelling[tt]
                      << "' in db pragma " << p << endl;
            return;
          }
          break;
        }
      }
    }

    if (tt != CPP_CLOSE_PAREN)
    {
      error (l) << "')' expected at the end of db pragma " << p << endl;
      return;
    }

    if (cp.name.empty ())
    {
      error (l) << "expected pointer name in db pragma " << p << endl;
      return;
    }

    cp.scope = current_scope ();
    cp.loc = loc;
    val = cp;

    tt = l.next (tl, &tn);
  }
  else if (p == "abstract")
  {
    // abstract
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    tt = l.next (tl, &tn);
  }
  else if (p == "optimistic")
  {
    // optimistic
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    tt = l.next (tl, &tn);
  }
  else if (p == "polymorphic")
  {
    // polymorphic
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    tt = l.next (tl, &tn);
  }
  else if (p == "definition")
  {
    // definition
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    val = l.location ();
    tt = l.next (tl, &tn);
  }
  else if (p == "callback")
  {
    // callback (name)
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    if (l.next (tl, &tn) != CPP_OPEN_PAREN)
    {
      error (l) << "'(' expected after db pragma " << p << endl;
      return;
    }

    tt = l.next (tl, &tn);

    if (tt != CPP_NAME)
    {
      error (l) << "member function name expected in db pragma " << p << endl;
      return;
    }

    val = tl;

    if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
    {
      error (l) << "')' expected at the end of db pragma " << p << endl;
      return;
    }

    tt = l.next (tl, &tn);
  }
  else if (p == "query")
  {
    // query ()
    // query ("statement")
    // query (expr)
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    if (l.next (tl, &tn) != CPP_OPEN_PAREN)
    {
      error (l) << "'(' expected after db pragma " << p << endl;
      return;
    }

    tt = l.next (tl, &tn);

    view_query vq;

    bool s (false);
    string str;

    if (tt == CPP_STRING)
    {
      s = true;
      str = tl;
      tt = l.next (tl, &tn);
    }

    if (tt == CPP_CLOSE_PAREN)
    {
      if (s)
        vq.literal = str;
      else
      {
        // Empty query() pragma indicates that the statement will be
        // provided at runtime. Encode this case as empty literal
        // and expression.
        //
      }
    }
    else
    {
      // Expression.
      //
      if (s)
        vq.expr.push_back (cxx_token (0, CPP_STRING, str));

      if (!parse_expression (l, tt, tl, tn, vq.expr, p))
        return; // Diagnostics has already been issued.
    }

    if (tt != CPP_CLOSE_PAREN)
    {
      error (l) << "')' expected at the end of db pragma " << p << endl;
      return;
    }

    vq.scope = current_scope ();
    vq.loc = loc;
    val = vq;
    tt = l.next (tl, &tn);
  }
  else if (p == "object")
  {
    // object (fq-name [ = name] [: expr])
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    if (l.next (tl, &tn) != CPP_OPEN_PAREN)
    {
      error (l) << "'(' expected after db pragma " << p << endl;
      return;
    }

    tt = l.next (tl, &tn);

    if (tt != CPP_NAME && tt != CPP_SCOPE)
    {
      error (l) << "type name expected in db pragma " << p << endl;
      return;
    }

    view_object vo;
    vo.kind = view_object::object;
    vo.obj_node = resolve_scoped_name (
      l, tt, tl, tn, current_scope (), vo.obj_name, true, p);

    if (vo.obj_node == 0)
      return; // Diagnostics has already been issued.

    // Get the actual type if this is a TYPE_DECL. Also get the main variant.
    //
    if (TREE_CODE (vo.obj_node) == TYPE_DECL)
      vo.obj_node = TREE_TYPE (vo.obj_node);

    if (TYPE_P (vo.obj_node)) // Can be a template.
      vo.obj_node = TYPE_MAIN_VARIANT (vo.obj_node);

    if (tt == CPP_EQ)
    {
      // We have an alias.
      //
      if (l.next (tl, &tn) != CPP_NAME)
      {
        error (l) << "alias name expected after '=' in db pragma " << p
                  << endl;
        return;
      }

      vo.alias = tl;
      tt = l.next (tl, &tn);
    }

    if (tt == CPP_COLON)
    {
      // We have a condition.

      tt = l.next (tl, &tn);

      if (!parse_expression (l, tt, tl, tn, vo.cond, p))
        return; // Diagnostics has already been issued.

      if (vo.cond.empty ())
      {
        error (l) << "join condition expected after ':' in db pragma " << p
                  << endl;
        return;
      }
    }

    if (tt != CPP_CLOSE_PAREN)
    {
      error (l) << "')' expected at the end of db pragma " << p << endl;
      return;
    }

    vo.scope = current_scope ();
    vo.loc = loc;
    val = vo;
    name = "objects"; // Change the context entry name.
    adder = &accumulate<view_object>;

    tt = l.next (tl, &tn);
  }
  else if (p == "id")
  {
    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    tt = l.next (tl, &tn);
  }
  else if (p == "no_id")
  {
    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    name = "id";
    val = false;

    tt = l.next (tl, &tn);
  }
  else if (p == "auto")
  {
    // auto
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    tt = l.next (tl, &tn);
  }
  else if (p == "column")
  {
    // column ("<name>")
    // column ("<name>.<name>")     (view only)
    // column ("<name>"."<name>")   (view only)
    // column (fq-name)             (view only)
    // column (expr)                (view only)
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    if (l.next (tl, &tn) != CPP_OPEN_PAREN)
    {
      error (l) << "'(' expected after db pragma " << p << endl;
      return;
    }

    tt = l.next (tl, &tn);

    bool expr (false);
    string expr_str;
    if (tt == CPP_STRING || tt == CPP_DOT)
    {
      qname qn;

      if (!parse_qname (l, tt, tl, tn, p, qn, &expr, &expr_str))
        return; // Diagnostics has already been issued.

      if (tt == CPP_CLOSE_PAREN)
      {
        table_column tc;
        tc.expr = expr;

        if (expr)
          tc.column = expr_str;
        else
        {
          tc.table = qn.qualifier ();
          tc.column = qn.uname ();
        }

        val = tc;
      }
      else if (!expr)
      {
        error (l) << "column name, expression, or data member name expected "
                  << "in db pragma " << p << endl;
        return;
      }
    }

    if (val.empty ())
    {
      // We have an expression.
      //
      column_expr e;

      if (expr)
      {
        e.push_back (column_expr_part ());
        e.back ().kind = column_expr_part::literal;
        e.back ().value = expr_str;

        if (tt != CPP_PLUS)
        {
          error (l) << "'+' or ')' expected in db pragma " << p << endl;
          return;
        }

        tt = l.next (tl, &tn);
      }

      for (;;)
      {
        if (tt == CPP_STRING)
        {
          e.push_back (column_expr_part ());
          e.back ().kind = column_expr_part::literal;
          e.back ().value = tl;

          tt = l.next (tl, &tn);
        }
        else if (tt == CPP_NAME || tt == CPP_SCOPE)
        {
          string name (parse_scoped_name (l, tt, tl, tn, p));

          if (name.empty ())
            return; // Diagnostics has already been issued.

          // Resolve nested members if any.
          //
          for (; tt == CPP_DOT; tt = l.next (tl, &tn))
          {
            if (l.next (tl, &tn) != CPP_NAME)
            {
              error (l) << "name expected after '.' in db pragma " << p
                        << endl;
              return;
            }

            name += '.';
            name += tl;
          }

          e.push_back (column_expr_part ());
          e.back ().kind = column_expr_part::reference;
          e.back ().value = name;
          e.back ().scope = current_scope ();
          e.back ().loc = loc;
        }
        else
        {
          error (l) << "column name, expression, or data member name expected "
                    << "in db pragma " << p << endl;
          return;
        }

        if (tt == CPP_PLUS)
          tt = l.next (tl, &tn);
        else if (tt == CPP_CLOSE_PAREN)
          break;
        else
        {
          error (l) << "'+' or ')' expected in db pragma " << p << endl;
          return;
        }
      }

      e.loc = loc;
      val = e;
      name = "column-expr";
    }

    tt = l.next (tl, &tn);
  }
  else if (p == "value_column" ||
           p == "index_column" ||
           p == "key_column" ||
           p == "id_column")
  {
    // value_column ("<name>")
    // index_column ("<name>")
    // key_column ("<name>")
    // id_column ("<name>")
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    if (l.next (tl, &tn) != CPP_OPEN_PAREN)
    {
      error (l) << "'(' expected after db pragma " << p << endl;
      return;
    }

    tt = l.next (tl, &tn);

    if (tt != CPP_STRING)
    {
      error (l) << "column name expected in db pragma " << p << endl;
      return;
    }

    val = tl;

    if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
    {
      error (l) << "')' expected at the end of db pragma " << p << endl;
      return;
    }

    tt = l.next (tl, &tn);
  }
  else if (p == "options" ||
           p == "value_options" ||
           p == "index_options" ||
           p == "key_options" ||
           p == "id_options")
  {
    // options (["<name>"])
    // value_options (["<name>"])
    // index_options (["<name>"])
    // key_options (["<name>"])
    // id_options (["<name>"])
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    if (l.next (tl, &tn) != CPP_OPEN_PAREN)
    {
      error (l) << "'(' expected after db pragma " << p << endl;
      return;
    }

    tt = l.next (tl, &tn);

    if (tt == CPP_STRING)
    {
      // Ignore empty options strings. Internally, we use them to
      // indicate options reset (see below).
      //
      if (!tl.empty ())
        val = tl;

      tt = l.next (tl, &tn);
    }
    else if (tt == CPP_CLOSE_PAREN)
    {
      // Empty options specifier signals options reset. Encode it as an
      // empty string.
      //
      val = string ();
    }
    else
    {
      error (l) << "options string expected in db pragma " << p << endl;
      return;
    }

    if (tt != CPP_CLOSE_PAREN)
    {
      error (l) << "')' expected at the end of db pragma " << p << endl;
      return;
    }

    adder = &accumulate<string>;
    tt = l.next (tl, &tn);
  }
  else if (p == "type" ||
           p == "id_type" ||
           p == "value_type" ||
           p == "index_type" ||
           p == "key_type")
  {
    // type ("<type>")
    // id_type ("<type>")
    // value_type ("<type>")
    // index_type ("<type>")
    // key_type ("<type>")
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    if (l.next (tl, &tn) != CPP_OPEN_PAREN)
    {
      error (l) << "'(' expected after db pragma " << p << endl;
      return;
    }

    tt = l.next (tl, &tn);

    if (tt != CPP_STRING)
    {
      error (l) << "type name expected in db pragma " << p << endl;
      return;
    }

    val = tl;

    if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
    {
      error (l) << "')' expected at the end of db pragma " << p << endl;
      return;
    }

    tt = l.next (tl, &tn);
  }
  else if (p == "null" ||
           p == "not_null" ||
           p == "value_null" ||
           p == "value_not_null")
  {
    // null
    // not_null
    // value_null
    // value_not_null
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    tt = l.next (tl, &tn);
  }
  else if (p == "default")
  {
    // default ()                  (<empty>)
    // default (null)              (n)
    // default (true|false)        (t|f)
    // default ([+|-]<number>)     (-|+)
    // default ("string")          (s)
    // default (<enumerator>)      (e)
    //
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    if (l.next (tl, &tn) != CPP_OPEN_PAREN)
    {
      error (l) << "'(' expected after db pragma " << p << endl;
      return;
    }

    tt = l.next (tl, &tn);

    default_value dv;

    switch (tt)
    {
    case CPP_CLOSE_PAREN:
      {
        // Default value reset.
        //
        dv.kind = default_value::reset;
        break;
      }
    case CPP_STRING:
      {
        dv.kind = default_value::string;
        dv.literal = tl;
        tt = l.next (tl, &tn);
        break;
      }
    case CPP_NAME:
      {
        // This can be null or an enumerator name.
        //
        if (tl == "null")
        {
          dv.kind = default_value::null;
          tt = l.next (tl, &tn);
          break;
        }
        // Fall throught.
      }
    case CPP_SCOPE:
      {
        // We have a potentially scopped enumerator name.
        //
        dv.enum_value = resolve_scoped_name (
          l, tt, tl, tn, current_scope (), dv.literal, false, p);

        if (dv.enum_value == 0)
          return; // Diagnostics has already been issued.

        dv.kind = default_value::enumerator;
        break;
      }
    case CPP_MINUS:
    case CPP_PLUS:
      {
        if (tt == CPP_MINUS)
          dv.literal = "-";

        tt = l.next (tl, &tn);

        if (tt != CPP_NUMBER)
        {
          error (l) << "expected numeric constant after '"
                    << (tt == CPP_MINUS ? "-" : "+") << "' in db pragma "
                    << p << endl;
          return;
        }

        // Fall through.
      }
    case CPP_NUMBER:
      {
        ///////
        switch (TREE_CODE (tn))
        {
        case INTEGER_CST:
          {
            HOST_WIDE_INT hwl (TREE_INT_CST_LOW (tn));
            HOST_WIDE_INT hwh (TREE_INT_CST_HIGH (tn));

            unsigned long long l (hwl);
            unsigned long long h (hwh);
            unsigned short width (HOST_BITS_PER_WIDE_INT);

            dv.int_value = (h << width) + l;
            dv.kind = default_value::integer;
            break;
          }
        case REAL_CST:
          {
            REAL_VALUE_TYPE d (TREE_REAL_CST (tn));

            if (REAL_VALUE_ISINF (d))
              dv.float_value = numeric_limits<double>::infinity ();
            else if (REAL_VALUE_ISNAN (d))
              dv.float_value = numeric_limits<double>::quiet_NaN ();
            else
            {
              char tmp[256];
              real_to_decimal (tmp, &d, sizeof (tmp), 0, true);
              istringstream is (tmp);
              is >> dv.float_value;
            }

            if (dv.literal == "-")
              dv.float_value = -dv.float_value;

            dv.kind = default_value::floating;
            break;
          }
        default:
          {
            error (l) << "unexpected numeric constant in db pragma " << p
                      << endl;
            return;
          }
        }

        tt = l.next (tl, &tn);
        break;
      }
    default:
      {
        // This can be the true or false keyword.
        //
        if (tt == CPP_KEYWORD && (tl == "true" || tl == "false"))
        {
          dv.kind = default_value::boolean;
          dv.literal = tl;
          tt = l.next (tl, &tn);
        }
        else
        {
          error (l) << "unexpected expression in db pragma " << p << endl;
          return;
        }

        break;
      }
    }

    if (tt != CPP_CLOSE_PAREN)
    {
      error (l) << "')' expected at the end of db pragma " << p << endl;
      return;
    }

    val = dv;
    tt = l.next (tl, &tn);
  }
  else if (p == "inverse")
  {
    // inverse (name)
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    if (l.next (tl, &tn) != CPP_OPEN_PAREN)
    {
      error (l) << "'(' expected after db pragma " << p << endl;
      return;
    }

    tt = l.next (tl, &tn);

    if (tt != CPP_NAME)
    {
      error (l) << "member name expected in db pragma " << p << endl;
      return;
    }

    val = tl;

    if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
    {
      error (l) << "')' expected at the end of db pragma " << p << endl;
      return;
    }

    tt = l.next (tl, &tn);
  }
  else if (p == "unordered")
  {
    // unordered
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    tt = l.next (tl, &tn);
  }
  else if (p == "readonly")
  {
    // readonly
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    tt = l.next (tl, &tn);
  }
  else if (p == "transient")
  {
    // transient
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    tt = l.next (tl, &tn);
  }
  else if (p == "version")
  {
    // version
    //

    // Make sure we've got the correct declaration type.
    //
    if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
      return;

    tt = l.next (tl, &tn);
  }
  else if (p == "virtual")
  {
    // Stray virtual specifier (i.e., without explicit member name).
    //
    error (l) << "virtual member declaration requires name" << endl;
    return;
  }
  else
  {
    error (l) << "unknown db pragma " << p << endl;
    return;
  }

  // Add the pragma unless was indicated otherwise.
  //
  if (!name.empty ())
  {
    // If the value is not specified and we don't use a custom adder,
    // then make it bool (flag).
    //
    if (adder == 0 && val.empty ())
      val = true;

    // Convert '_' to '-' in the context name so we get foo-bar instead
    // of foo_bar (that's the convention used).
    //
    for (size_t i (0); i < name.size (); ++i)
      if (name[i] == '_')
        name[i] = '-';

    // Record this pragma.
    //
    add_pragma (
      pragma (p, name, val, loc, &check_spec_decl_type, adder), decl, ns);
  }

  // See if there are any more pragmas.
  //
  if (tt == CPP_NAME || tt == CPP_KEYWORD)
  {
    handle_pragma (l,
                   tl,
                   qualifier,
                   qualifier_value,
                   decl,
                   decl_name,
                   ns);
  }
  else if (tt != CPP_EOF)
    error (l) << "unexpected text after " << p << " in db pragma" << endl;
}

//
// Qualifiers.
//

static bool
check_qual_decl_type (declaration const& d,
                      string const& name,
                      string const& p,
                      location_t l)
{
  int tc (d.tree_code ());
  bool type (TREE_CODE_CLASS (tc) == tcc_type);

  if (p == "map")
  {
    assert (d == global_namespace);
  }
  else if (p == "index")
  {
    if (tc != RECORD_TYPE)
    {
      // For an index, name is not empty only if the class name was
      // specified explicitly. Otherwise, the index definition scope
      // is assumed.
      //
      if (name.empty ())
      {
        error (l) << "db pragma " << p << " outside of a class scope" << endl;
        info (l) << "use the db pragma " << p << "(<class-name>) syntax "
                 << " instead" << endl;
      }
      else
        error (l) << "name '" << name << "' in db pragma " << p << " does "
                  << "not refer to a class" << endl;
      return false;
    }
  }
  else if (p == "namespace")
  {
    if (tc != NAMESPACE_DECL)
    {
      error (l) << "name '" << name << "' in db pragma " << p << " does "
                << "not refer to a namespace" << endl;
      return false;
    }
  }
  else if (p == "object" ||
           p == "view")
  {
    if (tc != RECORD_TYPE)
    {
      error (l) << "name '" << name << "' in db pragma " << p << " does "
                << "not refer to a class" << endl;
      return false;
    }
  }
  else if (p == "value")
  {
    if (!type)
    {
      error (l) << "name '" << name << "' in db pragma " << p << " does "
                << "not refer to a type" << endl;
      return false;
    }
  }
  else if (p == "member")
  {
    if (tc != FIELD_DECL)
    {
      error (l) << "name '" << name << "' in db pragma " << p << " does "
                << "not refer to a data member" << endl;
      return false;
    }
  }
  else
  {
    error (l) << "unknown db pragma " << p << endl;
    return false;
  }

  return true;
}

static void
add_qual_entry (compiler::context& ctx,
                string const& k,
                any const& v,
                location_t l)
{
  // Store the TYPE_DECL node that was referred to in the pragma. This
  // can be used later as a name hint in case the type is a template
  // instantiation. Also store the pragma location which is used as
  // the "definition point" for this instantiation.
  //
  ctx.set ("tree-node", v);
  ctx.set ("location", l);

  ctx.set (k, true);
}

static void
handle_pragma_qualifier (cxx_lexer& l, string const& p)
{
  cpp_ttype tt;
  string tl;
  tree tn;

  declaration decl;
  tree orig_decl (0);   // Original declarations as used in the pragma.
  string decl_name;

  string name (p);                 // Pragma name.
  any val;                         // Pragma value.
  location_t loc (l. location ()); // Pragma location.
  pragma::add_func adder (0);      // Custom context adder.
  bool ns (false);                 // Namespace location pragma.

  cxx_tokens saved_tokens;         // Saved token seuqnece to be replayed.

  // Pragma qualifiers.
  //
  if (p == "map")
  {
    // map type("<regex>") as("<subst>") [to("<subst>")] [from("<subst>")]
    //
    using relational::custom_db_type;

    custom_db_type ct;
    ct.loc = loc;
    val = ct;
    name = "custom-db-types";
    orig_decl = global_namespace;
    decl = declaration (orig_decl);
    adder = &accumulate<custom_db_type>;
    tt = l.next (tl, &tn);
  }
  else if (p == "index")
  {
    // Index can be both a qualifier and a specifier. Things are complicated
    // by the fact that when it is a specifier, it belongs to a member which
    // means that the actual qualifier ('member') can be omitted. So we need
    // to distinguish between cases like these:
    //
    // #pragma db index type("INTEGER")              // specifier
    // #pragma db index type("UNIQUE") member(foo_)  // qualifier
    //
    // The thing that determines whether this is a qualifier or a specifier
    // is the presence of the 'member' or 'members' specifier. So to handle
    // this we are going to pre-scan the pragma looking for 'member' or
    // 'members' and saving the tokens. Once we determine what this is,
    // we replay the saved tokens to actually parse them.
    //
    tt = l.next (tl, &tn);

    if (tt != CPP_OPEN_PAREN)
    {
      // Determine what this is by scanning the pragma until we see
      // the 'member' qualifier or EOF.
      //
      bool qual (false);
      size_t balance (0);

      for (; tt != CPP_EOF; tt = l.next (tl, &tn))
      {
        switch (tt)
        {
        case CPP_OPEN_PAREN:
          {
            balance++;
            break;
          }
        case CPP_CLOSE_PAREN:
          {
            if (balance > 0)
              balance--;
            else
            {
              error (l) << "unbalanced parenthesis in db pragma " << p << endl;
              return;
            }
            break;
          }
        case CPP_NAME:
          {
            if (balance == 0 && (tl == "member" || tl == "members"))
              qual = true;
            break;
          }
        default:
          break;
        }

        if (qual)
          break;

        saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
      }

      if (balance != 0)
      {
        error (l) << "unbalanced parenthesis in db pragma " << p << endl;
        return;
      }

      if (qual)
      {
        // This is a qualifer. The saved tokens sequence contains tokens
        // until the first 'member' or 'members' specifier. So we will
        // first need to re-play these tokens and then continue parsing
        // as if we just saw the 'member' or 'members' specifier. The
        // token type (tt) and token literal (tl) variables should contain
        // the correct values.
        //
        orig_decl = current_scope ();
        decl = declaration (orig_decl);
      }
      else
      {
        // This is a specifier. The saved tokens sequence contains all the
        // tokens in this pragma until EOF.
        //
        cxx_tokens_lexer l;
        l.start (saved_tokens, loc);
        handle_pragma (l, "index", "member", val, declaration (), "", false);
        return;
      }
    }

    relational::index in;
    in.loc = loc;

    if (tt == CPP_OPEN_PAREN)
    {
      // Specifier with the class fq-name, index name, or both.
      //
      // index(<fq-name>)
      // index("<name>")
      // index(<fq-name>::"<name>")
      //
      tt = l.next (tl, &tn);

      // Resolve class name, if any.
      //
      if (tt == CPP_NAME || tt == CPP_SCOPE)
      {
        cpp_ttype ptt;
        orig_decl = resolve_scoped_name (
          l, tt, tl, tn, current_scope (), decl_name, true, p, true, &ptt);

        if (orig_decl == 0)
          return; // Diagnostics has already been issued.

        // Get the actual type if this is a TYPE_DECL. Also get the main
        // variant.
        //
        if (TREE_CODE (orig_decl) == TYPE_DECL)
          orig_decl = TREE_TYPE (orig_decl);

        if (TYPE_P (orig_decl)) // Can be a template.
          decl = declaration (TYPE_MAIN_VARIANT (orig_decl));
        else
          decl = declaration (orig_decl);

        if (tt == CPP_STRING && ptt != CPP_SCOPE)
        {
          error (l) << "'::' expected before index name in db pragma " << p
                    << endl;
          return;
        }

        if (tt != CPP_STRING && ptt == CPP_SCOPE)
        {
          error (l) << "index name expected after '::' in db pragma " << p
                    << endl;
          return;
        }
      }
      else
      {
        orig_decl = current_scope ();
        decl = declaration (orig_decl);
      }

      // Make sure we've got the correct declaration type.
      //
      if (!check_qual_decl_type (decl, decl_name, p, loc))
        return;

      if (tt == CPP_STRING)
      {
        in.name = tl;
        tt = l.next (tl, &tn);
      }

      if (tt != CPP_CLOSE_PAREN)
      {
        error (l) << "')' expected at the end of db pragma " << p << endl;
        return;
      }

      tt = l.next (tl, &tn);
    }

    val = in;
    adder = &accumulate<relational::index>;
  }
  else if (p == "namespace")
  {
    // namespace [(<identifier>)]
    // namespace ()                (refers to global namespace)
    //

    tt = l.next (tl, &tn);

    if (tt == CPP_OPEN_PAREN)
    {
      tt = l.next (tl, &tn);

      if (tt == CPP_NAME || tt == CPP_SCOPE)
      {
        orig_decl = resolve_scoped_name (
          l, tt, tl, tn, current_scope (), decl_name, false, p);

        if (orig_decl == 0)
          return; // Diagnostics has already been issued.

        // Make sure we've got the correct declaration type.
        //
        if (!check_qual_decl_type (orig_decl, decl_name, p, loc))
          return;

        // Resolve namespace aliases if any.
        //
        orig_decl = ORIGINAL_NAMESPACE (orig_decl);
        decl = declaration (orig_decl);

        if (tt != CPP_CLOSE_PAREN)
        {
          error (l) << "')' expected at the end of db pragma " << p << endl;
          return;
        }

        tt = l.next (tl, &tn);
      }
      else if (tt == CPP_CLOSE_PAREN)
      {
        orig_decl = global_namespace;
        decl = declaration (orig_decl);
        tt = l.next (tl, &tn);
      }
      else
      {
        error (l) << "data member name expected in db pragma " << p << endl;
        return;
      }
    }
    else
    {
      // Make sure we are in a namespace scope.
      //
      if (TREE_CODE (current_scope ()) != NAMESPACE_DECL)
      {
        error (l) << "db pragma " << p << " is not in a namespace scope"
                  << endl;
        return;
      }

      ns = true;
    }
  }
  else if (p == "object" ||
           p == "view" ||
           p == "value")
  {
    // object [(<identifier>)]
    // view [(<identifier>)]
    // value [(<identifier>)]
    //

    tt = l.next (tl, &tn);

    if (tt == CPP_OPEN_PAREN)
    {
      tt = l.next (tl, &tn);

      // Can be built-in type (e.g., bool).
      //
      if (tt == CPP_NAME || tt == CPP_KEYWORD || tt == CPP_SCOPE)
      {
        orig_decl = resolve_scoped_name (
          l, tt, tl, tn, current_scope (), decl_name, true, p);

        if (orig_decl == 0)
          return; // Diagnostics has already been issued.

        // Get the actual type if this is a TYPE_DECL. Also get the main
        // variant.
        //
        if (TREE_CODE (orig_decl) == TYPE_DECL)
          orig_decl = TREE_TYPE (orig_decl);

        if (TYPE_P (orig_decl)) // Can be a template.
          decl = declaration (TYPE_MAIN_VARIANT (orig_decl));
        else
          decl = declaration (orig_decl);

        // Make sure we've got the correct declaration type.
        //
        if (!check_qual_decl_type (decl, decl_name, p, loc))
          return;

        if (tt != CPP_CLOSE_PAREN)
        {
          error (l) << "')' expected at the end of db pragma " << p << endl;
          return;
        }

        tt = l.next (tl, &tn);
      }
      else
      {
        error (l) << "type name expected in db pragma " << p << endl;
        return;
      }
    }
  }
  else if (p == "member")
  {
    // member [(<identifier>)]
    //

    tt = l.next (tl, &tn);

    if (tt == CPP_OPEN_PAREN)
    {
      tt = l.next (tl, &tn);

      if (tt != CPP_NAME && tt != CPP_SCOPE)
      {
        error (l) << "data member name expected in db pragma " << p << endl;
        return;
      }

      // We need to see if this is a virtual data member declaration. Also,
      // if it is not, then the name can still refer to one so we need to
      // take extra steps to handle that. But first, we save the name and
      // look for the 'virtual' specifier.
      //
      cxx_tokens name_tokens;
      for (; tt != CPP_CLOSE_PAREN && tt != CPP_EOF; tt = l.next (tl, &tn))
        name_tokens.push_back (cxx_token (l.location (), tt, tl, tn));

      if (tt != CPP_CLOSE_PAREN)
      {
        error (l) << "')' expected at the end of db pragma " << p << endl;
        return;
      }

      // Now scan the remainder of the pragma looking for the 'virtual'
      // keyword and saving the tokens in between for later.
      //
      bool virt (false);
      size_t balance (0);
      for (tt = l.next (tl, &tn); tt != CPP_EOF; tt = l.next (tl, &tn))
      {
        switch (tt)
        {
        case CPP_OPEN_PAREN:
          {
            balance++;
            break;
          }
        case CPP_CLOSE_PAREN:
          {
            if (balance > 0)
              balance--;
            else
            {
              error (l) << "unbalanced parenthesis in db pragma " << p << endl;
              return;
            }
            break;
          }
        default:
          {
            if (balance == 0 && tt == CPP_KEYWORD && tl == "virtual")
              virt = true;
            break;
          }
        }

        if (virt)
          break;

        saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
      }

      if (balance != 0)
      {
        error (l) << "unbalanced parenthesis in db pragma " << p << endl;
        return;
      }

      // Regardless of whether this is a virtual member declaration or a
      // reference, resolve its scope name (if one is specified), which
      // should be a class. We will need it in both cases.
      //
      tree scope;
      if (name_tokens.size () > 2) // scope::name
      {
        size_t n (name_tokens.size ());

        if (name_tokens[n - 2].type != CPP_SCOPE ||
            name_tokens[n - 1].type != CPP_NAME)
        {
          error (l) << "invalid name in db pragma " << p << endl;
          return;
        }

        cxx_tokens scope_tokens (1, name_tokens.back ());
        name_tokens.pop_back (); // ::
        name_tokens.pop_back (); // name
        name_tokens.swap (scope_tokens);

        cxx_tokens_lexer l;
        l.start (scope_tokens);

        tree tn;
        string tl;
        cpp_ttype tt (l.next (tl));

        scope = resolve_scoped_name (
          l, tt, tl, tn, current_scope (), decl_name, true, p);

        if (scope == 0)
          return; // Diagnostics has already been issued.

        scope = TREE_TYPE (scope);

        if (tt != CPP_EOF)
        {
          error (l) << "invalid name in db pragma " << p << endl;
          return;
        }

        decl_name += "::";
      }
      else
        scope = current_scope ();

      if (virt)
      {
        // Should be a single name.
        //
        if (name_tokens.size () > 1 || name_tokens.back ().type != CPP_NAME)
        {
          location_t l (name_tokens.back ().loc);
          error (l) << "invalid name in db pragma " << p << endl;
          return;
        }
        string const& name (name_tokens.back ().literal);

        // Parse the remainder of the virtual specifier.
        //
        // virtual(<fq-name>)
        //
        tree type;
        string type_name;
        {
          string p (tl);
          location_t loc (l.location ());

          if (l.next (tl, &tn) != CPP_OPEN_PAREN)
          {
            error (l) << "'(' expected after db pragma " << p << endl;
            return;
          }

          tt = l.next (tl, &tn);

          // Can be built-in type (e.g., bool).
          //
          if (tt == CPP_NAME || tt == CPP_KEYWORD || tt == CPP_SCOPE)
          {
            type = resolve_scoped_name (
              l, tt, tl, tn, current_scope (), type_name, true, p);

            if (type == 0)
              return; // Diagnostics has already been issued.

            if (TREE_CODE (type) != TYPE_DECL)
            {
              error (loc) << "name '" << type_name << "' in db pragma "
                          << p << " does not refer to a type" << endl;
              return;
            }

            type = TREE_TYPE (type);
          }
          else
          {
            error (l) << "type name expected in db pragma " << p << endl;
            return;
          }

          if (tt != CPP_CLOSE_PAREN)
          {
            error (l) << "')' expected at the end of db pragma " << p << endl;
            return;
          }

          tt = l.next (tl, &tn);
        }

        pair<virt_declaration_set::const_iterator, bool> r (
          virt_declarations_[scope].insert (
            virt_declaration (loc, name, FIELD_DECL, type)));

        if (!r.second)
        {
          error (loc) << "virtual data member declaration '" << name
                      << "' conflicts with a previous declaration" << endl;

          info (r.first->loc) << "'" << name << "' was previously "
                              << "declared here" << endl;
          return;
        }

        decl_name += name;
        decl = declaration (*r.first);

        // Mark it as virtual using the standard pragma machinery.
        //
        add_pragma (
          pragma ("virtual", "virtual", true, loc, &check_spec_decl_type, 0),
          decl,
          false);
      }
      else
      {
        // This is not a virtual member declaration but the name can
        // still refer to one. To handle this look for real and virtual
        // members in the scope that we just resolved.
        //
        if (name_tokens.size () == 1 && name_tokens.back ().type == CPP_NAME)
        {
          virt_declarations::iterator i (virt_declarations_.find (scope));

          if (i != virt_declarations_.end ())
          {
            virt_declaration_set::const_iterator j (
              i->second.find (name_tokens.back ().literal, FIELD_DECL));

            if (j != i->second.end ())
              decl = declaration (*j);
          }
        }

        if (!decl)
        {
          cxx_tokens_lexer l;
          l.start (name_tokens);

          tree tn;
          string tl;
          cpp_ttype tt (l.next (tl));

          orig_decl = resolve_scoped_name (
            l, tt, tl, tn, scope, decl_name, false, p);

          if (orig_decl == 0)
            return; // Diagnostics has already been issued.

          decl = declaration (orig_decl);
        }

        // Make sure we've got the correct declaration type.
        //
        if (!check_qual_decl_type (decl, decl_name, p, loc))
          return;
      }
    }
  }
  //
  // The member qualifier can be omitted so we need to also handle all
  // the member pragmas here.
  //
  else if (p == "id" ||
           p == "auto" ||
           p == "unique" ||
           p == "get" ||
           p == "set" ||
           p == "access" ||
           p == "column" ||
           p == "value_column" ||
           p == "index_column" ||
           p == "key_column" ||
           p == "id_column" ||
           p == "options" ||
           p == "value_options" ||
           p == "index_options" ||
           p == "key_options" ||
           p == "id_options" ||
           p == "type" ||
           p == "id_type" ||
           p == "value_type" ||
           p == "index_type" ||
           p == "key_type" ||
           p == "table" ||
           p == "null" ||
           p == "not_null" ||
           p == "value_null" ||
           p == "value_not_null" ||
           p == "default" ||
           p == "inverse" ||
           p == "unordered" ||
           p == "readonly" ||
           p == "transient" ||
           p == "version" ||
           p == "virtual")
  {
    handle_pragma (l, p, "member", val, declaration (), "", false);
    return;
  }
  else
  {
    error (l) << "unknown db pragma " << p << endl;
    return;
  }

  if (adder == 0)
    val = orig_decl;

  // Record this pragma.
  //
  pragma prag (p,
               name, // For now no need to translate '_' to '-'.
               val,
               loc,
               &check_qual_decl_type,
               adder != 0 ? adder : &add_qual_entry);

  any* pval;

  if (decl)
    pval = &decl_pragmas_[decl].insert (prag).value;
  else
  {
    tree scope (current_scope ());

    if (!ns)
    {
      if (!CLASS_TYPE_P (scope))
        scope = global_namespace;

      pragma_list& pl (loc_pragmas_[scope]);
      pl.push_back (prag);
      pval = &pl.back ().value;
    }
    else
    {
      ns_loc_pragmas_.push_back (ns_loc_pragma (scope, prag));
      pval = &ns_loc_pragmas_.back ().pragma.value;
    }
  }

  // See if there are any saved tokens to replay.
  //
  if (!saved_tokens.empty ())
  {
    cxx_tokens_lexer l;
    l.start (saved_tokens);

    string tl;
    cpp_ttype tt (l.next (tl));

    if (tt == CPP_NAME || tt == CPP_KEYWORD)
    {
      handle_pragma (l, tl, p, *pval, decl, decl_name, ns);

      if (errorcount != 0) // Avoid parsing the rest if there was an error.
        return;
    }
    else if (tt != CPP_EOF)
    {
      error (l) << "unexpected text after " << p << " in db pragma" << endl;
      return;
    }
  }

  if (tt == CPP_NAME || tt == CPP_KEYWORD)
  {
    handle_pragma (l, tl, p, *pval, decl, decl_name, ns);
  }
  else if (tt != CPP_EOF)
    error (l) << "unexpected text after " << p << " in db pragma" << endl;
}

/*
extern "C" void
handle_pragma_db_map (cpp_reader* r)
{
  handle_pragma_qualifier (r, "map");
}

extern "C" void
handle_pragma_db_namespace (cpp_reader* r)
{
  handle_pragma_qualifier (r, "namespace");
}

extern "C" void
handle_pragma_db_object (cpp_reader* r)
{
  handle_pragma_qualifier (r, "object");
}

extern "C" void
handle_pragma_db_view (cpp_reader* r)
{
  handle_pragma_qualifier (r, "view");
}

extern "C" void
handle_pragma_db_value (cpp_reader* r)
{
  handle_pragma_qualifier (r, "value");
}

extern "C" void
handle_pragma_db_member (cpp_reader* r)
{
  handle_pragma_qualifier (r, "member");
}

extern "C" void
handle_pragma_db_id (cpp_reader* r)
{
  handle_pragma_qualifier (r, "id");
}

extern "C" void
handle_pragma_db_auto (cpp_reader* r)
{
  handle_pragma_qualifier (r, "auto");
}

extern "C" void
handle_pragma_db_column (cpp_reader* r)
{
  handle_pragma_qualifier (r, "column");
}

extern "C" void
handle_pragma_db_vcolumn (cpp_reader* r)
{
  handle_pragma_qualifier (r, "value_column");
}

extern "C" void
handle_pragma_db_icolumn (cpp_reader* r)
{
  handle_pragma_qualifier (r, "index_column");
}

extern "C" void
handle_pragma_db_kcolumn (cpp_reader* r)
{
  handle_pragma_qualifier (r, "key_column");
}

extern "C" void
handle_pragma_db_idcolumn (cpp_reader* r)
{
  handle_pragma_qualifier (r, "id_column");
}

extern "C" void
handle_pragma_db_options (cpp_reader* r)
{
  handle_pragma_qualifier (r, "options");
}

extern "C" void
handle_pragma_db_voptions (cpp_reader* r)
{
  handle_pragma_qualifier (r, "value_options");
}

extern "C" void
handle_pragma_db_ioptions (cpp_reader* r)
{
  handle_pragma_qualifier (r, "index_options");
}

extern "C" void
handle_pragma_db_koptions (cpp_reader* r)
{
  handle_pragma_qualifier (r, "key_options");
}

extern "C" void
handle_pragma_db_idoptions (cpp_reader* r)
{
  handle_pragma_qualifier (r, "id_options");
}

extern "C" void
handle_pragma_db_type (cpp_reader* r)
{
  handle_pragma_qualifier (r, "type");
}

extern "C" void
handle_pragma_db_id_type (cpp_reader* r)
{
  handle_pragma_qualifier (r, "id_type");
}

extern "C" void
handle_pragma_db_vtype (cpp_reader* r)
{
  handle_pragma_qualifier (r, "value_type");
}

extern "C" void
handle_pragma_db_itype (cpp_reader* r)
{
  handle_pragma_qualifier (r, "index_type");
}

extern "C" void
handle_pragma_db_ktype (cpp_reader* r)
{
  handle_pragma_qualifier (r, "key_type");
}

extern "C" void
handle_pragma_db_table (cpp_reader* r)
{
  handle_pragma_qualifier (r, "table");
}

extern "C" void
handle_pragma_db_null (cpp_reader* r)
{
  handle_pragma_qualifier (r, "null");
}

extern "C" void
handle_pragma_db_not_null (cpp_reader* r)
{
  handle_pragma_qualifier (r, "not_null");
}

extern "C" void
handle_pragma_db_value_null (cpp_reader* r)
{
  handle_pragma_qualifier (r, "value_null");
}

extern "C" void
handle_pragma_db_value_not_null (cpp_reader* r)
{
  handle_pragma_qualifier (r, "value_not_null");
}

extern "C" void
handle_pragma_db_default (cpp_reader* r)
{
  handle_pragma_qualifier (r, "default");
}

extern "C" void
handle_pragma_db_inverse (cpp_reader* r)
{
  handle_pragma_qualifier (r, "inverse");
}

extern "C" void
handle_pragma_db_unordered (cpp_reader* r)
{
  handle_pragma_qualifier (r, "unordered");
}

extern "C" void
handle_pragma_db_readonly (cpp_reader* r)
{
  handle_pragma_qualifier (r, "readonly");
}

extern "C" void
handle_pragma_db_transient (cpp_reader* r)
{
  handle_pragma_qualifier (r, "transient");
}

extern "C" void
handle_pragma_db_version (cpp_reader* r)
{
  handle_pragma_qualifier (r, "version");
}

extern "C" void
handle_pragma_db_virtual (cpp_reader* r)
{
  handle_pragma_qualifier (r, "virtual");
}
*/

extern "C" void
handle_pragma_db (cpp_reader*)
{
  cxx_pragma_lexer l;
  l.start ();

  string tl;
  cpp_ttype tt (l.next (tl));

  if (tt != CPP_NAME && tt != CPP_KEYWORD)
  {
    if (tt == CPP_EOF)
      error (l) << "expected specifier after db pragma" << endl;
    else
      error (l) << "unexpected token after db pragma" << endl;
    return;
  }

  handle_pragma_qualifier (l, tl);
}

extern "C" void
register_odb_pragmas (void*, void*)
{
  // Initialize the list of simple value pragmas.
  //
  //@@ Did we add new simple value pragmas and forgot to account for
  //   them here?
  //
  simple_value_pragmas_.insert ("table");
  simple_value_pragmas_.insert ("type");
  simple_value_pragmas_.insert ("id-type");
  simple_value_pragmas_.insert ("value-type");
  simple_value_pragmas_.insert ("index-type");
  simple_value_pragmas_.insert ("key-type");
  simple_value_pragmas_.insert ("value-column");
  simple_value_pragmas_.insert ("index-column");
  simple_value_pragmas_.insert ("key-column");
  simple_value_pragmas_.insert ("id-column");
  simple_value_pragmas_.insert ("default");
  simple_value_pragmas_.insert ("null");
  simple_value_pragmas_.insert ("not-null");
  simple_value_pragmas_.insert ("value-null");
  simple_value_pragmas_.insert ("value-not-null");
  simple_value_pragmas_.insert ("options");
  simple_value_pragmas_.insert ("value-options");
  simple_value_pragmas_.insert ("index-options");
  simple_value_pragmas_.insert ("key-options");
  simple_value_pragmas_.insert ("id-options");
  simple_value_pragmas_.insert ("unordered");


  // GCC has a limited number of pragma slots and we have exhausted them.
  // A workaround is to make 'db' a pragma rather than a namespace. This
  // way we only have one pragma but the drawback of this approach is the
  // fact that the specifier or qualifier name will now be macro-expanded
  // (though this happens anyway if we have multiple specifiers in a single
  // pragma). Once the GCC folks fix this, we can go back to the namespace
  // approach.
  //
  c_register_pragma_with_expansion (0, "db", handle_pragma_db);

  /*
  c_register_pragma_with_expansion ("db", "map", handle_pragma_db_map);
  c_register_pragma_with_expansion ("db", "namespace", handle_pragma_db_namespace);
  c_register_pragma_with_expansion ("db", "object", handle_pragma_db_object);
  c_register_pragma_with_expansion ("db", "view", handle_pragma_db_view);
  c_register_pragma_with_expansion ("db", "value", handle_pragma_db_value);
  c_register_pragma_with_expansion ("db", "member", handle_pragma_db_member);
  c_register_pragma_with_expansion ("db", "id", handle_pragma_db_id);
  c_register_pragma_with_expansion ("db", "auto", handle_pragma_db_auto);
  c_register_pragma_with_expansion ("db", "column", handle_pragma_db_column);
  c_register_pragma_with_expansion ("db", "value_column", handle_pragma_db_vcolumn);
  c_register_pragma_with_expansion ("db", "index_column", handle_pragma_db_icolumn);
  c_register_pragma_with_expansion ("db", "key_column", handle_pragma_db_kcolumn);
  c_register_pragma_with_expansion ("db", "id_column", handle_pragma_db_idcolumn);
  c_register_pragma_with_expansion ("db", "options", handle_pragma_db_options);
  c_register_pragma_with_expansion ("db", "value_options", handle_pragma_db_voptions);
  c_register_pragma_with_expansion ("db", "index_options", handle_pragma_db_ioptions);
  c_register_pragma_with_expansion ("db", "key_options", handle_pragma_db_koptions);
  c_register_pragma_with_expansion ("db", "id_options", handle_pragma_db_idoptions);
  c_register_pragma_with_expansion ("db", "type", handle_pragma_db_type);
  c_register_pragma_with_expansion ("db", "id_type", handle_pragma_db_id_type);
  c_register_pragma_with_expansion ("db", "value_type", handle_pragma_db_vtype);
  c_register_pragma_with_expansion ("db", "index_type", handle_pragma_db_itype);
  c_register_pragma_with_expansion ("db", "key_type", handle_pragma_db_ktype);
  c_register_pragma_with_expansion ("db", "table", handle_pragma_db_table);
  c_register_pragma_with_expansion ("db", "null", handle_pragma_db_null);
  c_register_pragma_with_expansion ("db", "not_null", handle_pragma_db_not_null);
  c_register_pragma_with_expansion ("db", "value_null", handle_pragma_db_value_null);
  c_register_pragma_with_expansion ("db", "value_not_null", handle_pragma_db_value_not_null);
  c_register_pragma_with_expansion ("db", "default", handle_pragma_db_default);
  c_register_pragma_with_expansion ("db", "inverse", handle_pragma_db_inverse);
  c_register_pragma_with_expansion ("db", "unordered", handle_pragma_db_unordered);
  c_register_pragma_with_expansion ("db", "readonly", handle_pragma_db_readonly);
  c_register_pragma_with_expansion ("db", "transient", handle_pragma_db_transient);
  c_register_pragma_with_expansion ("db", "version", handle_pragma_db_version);
  c_register_pragma_with_expansion ("db", "virtual", handle_pragma_db_virtual);
  */
}

void
post_process_pragmas ()
{
  // Make sure composite class template instantiations are fully
  // instantiated.
  //
  for (decl_pragmas::iterator i (decl_pragmas_.begin ()),
         e (decl_pragmas_.end ()); i != e; ++i)
  {
    if (i->first.virt)
      continue;

    tree type (i->first.decl.real);

    if (!(CLASS_TYPE_P (type) && CLASSTYPE_TEMPLATE_INSTANTIATION (type)))
      continue;

    // Check whether this is a composite value type. We don't want to
    // instantiate simple values since they may be incomplete.
    //
    pragma const* p (0);

    for (pragma_set::iterator j (i->second.begin ()), e (i->second.end ());
         j != e; ++j)
    {
      string const& name (j->context_name);

      if (name == "value")
        p = &*j;
      else
      {
        // Make sure it is not one of the simple value pragmas.
        //
        if (simple_value_pragmas_.find (name) != simple_value_pragmas_.end ())
        {
          p = 0;
          break;
        }
      }
    }

    if (p == 0)
      continue;

    // Make sure it is instantiated.
    //
    tree decl (TYPE_NAME (p->value.value<tree> ()));
    location_t loc (DECL_SOURCE_LOCATION (decl));

    // Reset input location so that we get nice diagnostics in case
    // of an error.
    //
    input_location = loc;

    if (instantiate_class_template (type) == error_mark_node ||
        errorcount != 0 ||
        !COMPLETE_TYPE_P (type))
    {
      error (loc) << "unable to instantiate composite value class template"
                  << endl;

      throw pragmas_failed ();
    }
  }
}
