// file      : odb/pragma.cxx
// copyright : Copyright (c) 2009-2015 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_;

database pragma_db_;
multi_database pragma_multi_;

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 mis-matching ')' or ',' while
  // keeping track of the '()' 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_COMMA:
      {
        if (balance == 0)
          done = true;
        else
          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));
            unsigned long long v (integer_value (tn));

            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)
{
  gcc_tree_code_type 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 class" << endl;
      return false;
    }
  }
  else if (p == "id"        ||
           p == "auto"      ||
           p == "column"    ||
           p == "inverse"   ||
           p == "on_delete" ||
           p == "section"   ||
           p == "load"      ||
           p == "update"    ||
           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 == "added")
  {
    // Added can be used for data members only.
    //
    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 == "deleted")
  {
    // Deleted can be used for both data members and classes (object,
    // view of 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" ||
           p == "sectionable" ||
           p == "bulk")
  {
    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 if (p == "simple" ||
           p == "container")
  {
    // Apply to 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
  {
    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 db,
               string 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;

  // See if there is a database prefix. The db argument may already
  // contain it.
  //
  if (db.empty () &&
      (p == "mysql"  ||
       p == "sqlite" ||
       p == "pgsql"  ||
       p == "oracle" ||
       p == "mssql"))
  {
    tt = l.next (tl);

    if (tt != CPP_COLON)
    {
      error (l) << "':' expected after database prefix " << p << endl;
      return;
    }

    // Specifier prefix.
    //
    db = p;
    tt = l.next (p);

    if (tt != CPP_NAME && tt != CPP_KEYWORD)
    {
      error (l) << "expected specifier after databas prefix " << db << endl;
      return;
    }
  }

  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 == "model")
  {
    // version(unsigned long long base,
    //         unsigned long long current,
    //         open|closed)
    //

    // Make sure we've got the correct declaration type.
    //
    assert (decl == global_namespace);

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

      model_version v;

      // base
      //
      if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST)
      {
        error (l) << "unsigned integer expected as base version" << endl;
        return;
      }

      v.base = integer_value (tn);

      if (v.base == 0)
      {
        error (l) << "base version cannot be zero" << endl;
        return;
      }

      // current
      //
      if (l.next (tl, &tn) != CPP_COMMA)
      {
        error (l) << "current version expected after base version" << endl;
        return;
      }

      if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST)
      {
        error (l) << "unsigned integer expected as current version" << endl;
        return;
      }

      v.current = integer_value (tn);

      if (v.current == 0)
      {
        error (l) << "current version cannot be zero" << endl;
        return;
      }

      if (v.base > v.current)
      {
        error (l) << "current version should be greater than or equal to " <<
          "base version" << endl;
        return;
      }

      // open|closed
      //
      tt = l.next (tl, &tn);
      if (tt == CPP_COMMA)
      {
        if (l.next (tl, &tn) != CPP_NAME || (tl != "open" && tl != "closed"))
        {
          error (l) << "open or closed expected after current version" << endl;
          return;
        }

        v.open = (tl == "open");
        tt = l.next (tl, &tn);
      }
      else
        v.open = true;

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

      name = "model-version";
      val = v;
      tt = l.next (tl, &tn);
    }
    else
    {
      error (l) << "unknown db pragma " << p << endl;
      return;
    }
  }
  else 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;
    }

    member_access ma (loc, p == "set" ? "modifier" : "accessor", false);

    tt = l.next (tl, &tn);
    if (tt != CPP_CLOSE_PAREN) // Empty expression are ok.
    {
      if (!parse_expression (l, tt, tl, tn, ma.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;
      }
    }

    val = ma;

    // Convert access to the get/set pair.
    //
    if (p == "access")
    {
      if (db.empty () || db == pragma_db_.string ())
        add_pragma (
          pragma (p, "get", val, loc, &check_spec_decl_type, 0), decl, ns);

      ma.kind = "modifier";
      val = ma;
      name = "set";
    }

    tt = l.next (tl, &tn);
  }
  else if (p == "table")
  {
    // table (<name>)
    // table (<name> [= "<alias>"] [type] [: "<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_NAME)
    {
      // We have a JOIN type.
      //
      if (tl == "left")
        vo.join = view_object::left;
      else if (tl == "right")
        vo.join = view_object::right;
      else if (tl == "full")
        vo.join = view_object::full;
      else if (tl == "inner")
        vo.join = view_object::inner;
      else if (tl == "cross")
        vo.join = view_object::cross;
      else
      {
        error (l) << "unknown join type '" << tl << "'" << endl;
        return;
      }

      tt = l.next (tl, &tn);
    }
    else
      vo.join = view_object::left;

    if (tt == CPP_COLON)
    {
      // We have a condition.
      //
      if (vo.join == view_object::cross)
      {
        error (l)
          << "no join condition can be specified for a cross join" << endl;
        return;
      }

      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 ())
    {
      if (db.empty () || db == pragma_db_.string ())
      {
        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 == "sectionable")
  {
    // sectionable
    //

    // 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 == "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 == "bulk")
  {
    // bulk (batch-size)
    //

    // 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;
    }

    // base
    //
    if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST)
    {
      error (l) << "unsigned integer expected as batch size" << endl;
      return;
    }

    unsigned long long b (integer_value (tn));

    if (b == 0 || b == 1)
    {
      error (l) << "batch size has to be greater than 1" << endl;
      return;
    }

    val = b;

    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.
    }

    // Disallow query(, distinct).
    //
    if (tt == CPP_COMMA && vq.expr.empty ())
    {
      error (l) << "query expression expected in db pragma " << p << endl;
      return;
    }

    // The query expression can be omitted with the modifier in its
    // place. Handle this case.
    //
    if (vq.expr.size () == 1 && vq.expr.front ().type == CPP_NAME)
    {
      string const& n (vq.expr.front ().literal);

      if (n == "distinct")
        vq.distinct = true;
      else if (n == "for_update")
        vq.for_update = true;

      if (vq.distinct || vq.for_update)
        vq.expr.clear ();
    }

    if (tt == CPP_COMMA)
    {
      do
      {
        if (l.next (tl, &tn) != CPP_NAME)
        {
          error (l) << "result modifier expected in db pragma " << p << endl;
          return;
        }

        if (tl == "distinct")
          vq.distinct = true;
        else if (tl == "for_update")
          vq.for_update = true;
        else
        {
          error (l) << "unknown result modifier '" << tl << "'" << endl;
          return;
        }

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

      } while (tt == CPP_COMMA);
    }

    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] [type] [: 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_NAME)
    {
      // We have a JOIN type.
      //
      if (tl == "left")
        vo.join = view_object::left;
      else if (tl == "right")
        vo.join = view_object::right;
      else if (tl == "full")
        vo.join = view_object::full;
      else if (tl == "inner")
        vo.join = view_object::inner;
      else if (tl == "cross")
        vo.join = view_object::cross;
      else
      {
        error (l) << "unknown join type '" << tl << "'" << endl;
        return;
      }

      tt = l.next (tl, &tn);
    }
    else
      vo.join = view_object::left;

    if (tt == CPP_COLON)
    {
      // We have a condition.
      //
      if (vo.join == view_object::cross)
      {
        error (l)
          << "no join condition can be specified for a cross join" << endl;
        return;
      }

      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:
          {
            dv.int_value = integer_value (tn);
            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 == "on_delete")
  {
    // on_delete (cascade|set_null)
    //

    // 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;
    }

    if (l.next (tl, &tn) != CPP_NAME || (tl != "cascade" && tl != "set_null"))
    {
      error (l) << "cascade or set_null expected after '('" << endl;
      return;
    }

    using semantics::relational::foreign_key;
    val = (tl == "cascade" ? foreign_key::cascade : foreign_key::set_null);

    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 == "section")
  {
    // section (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;
    }

    name = "section-member";
    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 == "load")
  {
    // load (eager|lazy)
    //

    // 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 || (tl != "eager" && tl != "lazy"))
    {
      error (l) << "eager or lazy expected in db pragma " << p << endl;
      return;
    }

    name = "section-load";
    val = (tl == "eager"
           ? user_section::load_eager
           : user_section::load_lazy);

    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 == "update")
  {
    // update (always|change|manual)
    //

    // 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 ||
        (tl != "always" && tl != "change" && tl != "manual"))
    {
      error (l) << "always, change, or manual expected in db pragma " <<
        p << endl;
      return;
    }

    name = "section-update";

    if (tl == "always")
      val = user_section::update_always;
    else if (tl == "change")
      val = user_section::update_change;
    else
      val = user_section::update_manual;

    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 == "added" || p == "deleted")
  {
    // added (unsigned long long version)
    // deleted (unsigned long long version)
    //

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

    char const* n (p == "added" ? "addition" : "deletion");

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

    if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST)
    {
      error (l) << "unsigned integer expected as " << n << " version" << endl;
      return;
    }

    unsigned long long v (integer_value (tn));

    if (v == 0)
    {
      error (l) << n << " version cannot be zero" << endl;
      return;
    }

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

    val = v;
    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 () && (db.empty () || db == pragma_db_.string ()))
  {
    // 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);
  }

  // Mark the type or member as simple value or container, depending
  // on the pragma. For static multi-database support we only do it
  // if the pragma applies to this database since in this case we can
  // have different mappings for different databases (e.g., composite
  // in one and simple in another). For dynamic multi-database support
  // we do this regardless of the database since here the mapping
  // should the consistent.
  //
  // @@ Did we add new simple value or container pragmas and forgot to
  //    account for them here?
  //
  if ((qualifier == "value" || qualifier == "member") &&
      (pragma_multi_ == multi_database::dynamic || db.empty () ||
       db == pragma_db_.string ()))
  {
    // We assume a data member is simple only if the database type was
    // specified explicitly.
    //
    if (name == "type"     ||
        name == "id-type"  ||
        (qualifier == "value" &&
         (name == "null"     ||
          name == "not-null" ||
          name == "default"  ||
          name == "options")))
    {
      add_pragma (pragma (p, "simple", true, loc, &check_spec_decl_type, 0),
                  decl,
                  false);
    }
    else if (name == "table"      ||
             name == "value-type" ||
             name == "index-type" ||
             name == "key-type"   ||

             name == "value-null" ||
             name == "value-not-null" ||

             name == "value-column" ||
             name == "index-column" ||
             name == "key-column"   ||
             name == "index-column" ||

             name == "value-options" ||
             name == "index-options" ||
             name == "key-options"   ||
             name == "index-options" ||

             name == "unordered")
    {
      add_pragma (pragma (p, "container", true, loc, &check_spec_decl_type, 0),
                  decl,
                  false);
    }
  }

  // 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)
{
  gcc_tree_code_type tc (d.tree_code ());
  bool type (TREE_CODE_CLASS (tc) == tcc_type);

  if (p == "model" ||
      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 p)
{
  cpp_ttype tt;
  string tl;
  tree tn;

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

  // Check for a database prefix.
  //
  string db;

  if (p == "mysql"  ||
      p == "sqlite" ||
      p == "pgsql"  ||
      p == "oracle" ||
      p == "mssql")
  {
    tt = l.next (tl);

    if (tt == CPP_COLON)
    {
      // Specifier prefix.
      //
      db = p;
      tt = l.next (p);
    }
    else
    {
      // Qualifier prefix. Ignore the rest if the databases don't match.
      //
      if (p != pragma_db_.string ())
        return;

      p = tl;
    }

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

    // Make sure a qualifier prefix is not used before a specifier.
    //
    if (!db.empty () &&
        (p == "model"     ||
         p == "map"       ||
         p == "namespace" ||
         p == "object"    ||
         p == "view"      ||
         p == "value"     ||
         p == "member"))
    {
      error (l) << "specifier prefix '" << db << ":' used before qualifier " <<
        p << endl;
      return;
    }
  }

  //
  //
  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 == "model")
  {
    orig_decl = global_namespace;
    decl = declaration (orig_decl);
    tt = l.next (tl, &tn);
  }
  else 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.
        //

        // Also check that no specifier prefix was used for this qualifer.
        //
        if (!db.empty ())
        {
          error (loc) << "specifier prefix '" << db << ":' used before " <<
            "qualifier index" << endl;
          return;
        }

        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, db, "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 = TYPE_MAIN_VARIANT (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 == "section" ||
           p == "load"    ||
           p == "update"  ||
           p == "inverse" ||
           p == "on_delete" ||
           p == "unordered" ||
           p == "readonly" ||
           p == "transient" ||
           p == "added" ||
           p == "deleted" ||
           p == "version" ||
           p == "virtual")
  {
    handle_pragma (l, db, p, "member", val, declaration (), "", false);
    return;
  }
  else
  {
    error (l) << "unknown db pragma " << p << endl;
    return;
  }

  // Record this pragma. Delay this until after we process the
  // specifiers for value (see comment below for the reason).
  //
  if (adder == 0)
    val = orig_decl;

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

  tree scope;
  if (!decl)
  {
    scope = current_scope ();

    if (!ns && !CLASS_TYPE_P (scope))
      scope = global_namespace;
  }

  any* pval;
  if (p != "value")
  {
    if (decl)
      pval = &decl_pragmas_[decl].insert (prag).value;
    else
    {
      if (!ns)
      {
        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;
      }
    }
  }
  else
    pval = &val;

  // 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;
    }
  }

  size_t count (0);
  if (tt == CPP_NAME || tt == CPP_KEYWORD)
  {
    if (decl)
      count = decl_pragmas_[decl].size ();
    else
      count = loc_pragmas_[scope].size ();

    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;
    return;
  }

  // Record the value pragma. Here things are complicated by the fact
  // that we use the value pragma by itself to signify a composite value
  // type declaration. Consider this example:
  //
  // #pragma db value pgsql:type("POINT")
  // class point {...};
  //
  // Should this class be considered composite value type in other
  // databases (because that's what would happen by default)? Probably
  // not. So to overcome this we are going to detect and ignore cases
  // where (a) some specifiers followed the value qualifier but (b)
  // none of them are for the database that we are compiling for.
  //
  if (p == "value")
  {
    if (decl)
    {
      pragma_set& ps (decl_pragmas_[decl]);

      if (tt == CPP_EOF || ps.size () > count)
        ps.insert (prag);
    }
    else
    {
      pragma_list& pl (loc_pragmas_[scope]);

      if (tt == CPP_EOF || pl.size () > count)
        pl.push_back (prag);
    }
  }
}

/*
extern "C" void
handle_pragma_db_mysql (cpp_reader* r)
{
  handle_pragma_qualifier (r, "mysql");
}

extern "C" void
handle_pragma_db_sqlite (cpp_reader* r)
{
  handle_pragma_qualifier (r, "sqlite");
}

extern "C" void
handle_pragma_db_pgsql (cpp_reader* r)
{
  handle_pragma_qualifier (r, "pgsql");
}

extern "C" void
handle_pragma_db_oracle (cpp_reader* r)
{
  handle_pragma_qualifier (r, "oracle");
}

extern "C" void
handle_pragma_db_mssql (cpp_reader* r)
{
  handle_pragma_qualifier (r, "mssql");
}

extern "C" void
handle_pragma_db_model (cpp_reader* r)
{
  handle_pragma_qualifier (r, "model");
}

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_section (cpp_reader* r)
{
  handle_pragma_qualifier (r, "section");
}

extern "C" void
handle_pragma_db_load (cpp_reader* r)
{
  handle_pragma_qualifier (r, "load");
}

extern "C" void
handle_pragma_db_update (cpp_reader* r)
{
  handle_pragma_qualifier (r, "update");
}

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

extern "C" void
handle_pragma_db_on_delete (cpp_reader* r)
{
  handle_pragma_qualifier (r, "on_delete");
}

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_added (cpp_reader* r)
{
  handle_pragma_qualifier (r, "added");
}

extern "C" void
handle_pragma_db_deleted (cpp_reader* r)
{
  handle_pragma_qualifier (r, "deleted");
}

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)
  {
    error (l) << "expected specifier after db pragma" << endl;
    return;
  }

  handle_pragma_qualifier (l, tl);
}

extern "C" void
register_odb_pragmas (void*, void*)
{
  // 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", "mysql", handle_pragma_db_mysql);
  c_register_pragma_with_expansion ("db", "sqlite", handle_pragma_db_sqlite);
  c_register_pragma_with_expansion ("db", "pgsql", handle_pragma_db_pgsql);
  c_register_pragma_with_expansion ("db", "oracle", handle_pragma_db_oracle);
  c_register_pragma_with_expansion ("db", "mssql", handle_pragma_db_mssql);
  c_register_pragma_with_expansion ("db", "model", handle_pragma_db_model);
  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", "section", handle_pragma_db_section);
  c_register_pragma_with_expansion ("db", "load", handle_pragma_db_load);
  c_register_pragma_with_expansion ("db", "update", handle_pragma_db_update);
  c_register_pragma_with_expansion ("db", "inverse", handle_pragma_db_inverse);
  c_register_pragma_with_expansion ("db", "on_delete", handle_pragma_db_on_delete);
  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", "added", handle_pragma_db_added);
  c_register_pragma_with_expansion ("db", "deleted", handle_pragma_db_deleted);
  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 object and 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 an object or composite value type.
    //
    pragma const* p (0);
    string diag_name;

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

      if (name == "object")
      {
        p = &*j;
        diag_name = "persistent object";
        break;
      }
      else if (name == "value")
      {
        p = &*j;
        diag_name = "composite value";
      }
      // We don't want to instantiate simple values since they may be
      // incomplete.
      //
      else if (name == "simple" || name == "container")
      {
        p = 0;
        break;
      }
    }

    if (p == 0)
      continue;

    // Make sure it is instantiated.
    //
    tree decl (TYPE_NAME (p->value.value<tree> ()));
    location_t loc (real_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 " << diag_name << " class template"
                  << endl;
      throw pragmas_failed ();
    }
  }
}
