summaryrefslogtreecommitdiff
path: root/odb/odb/pragma.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'odb/odb/pragma.cxx')
-rw-r--r--odb/odb/pragma.cxx4442
1 files changed, 4442 insertions, 0 deletions
diff --git a/odb/odb/pragma.cxx b/odb/odb/pragma.cxx
new file mode 100644
index 0000000..6668733
--- /dev/null
+++ b/odb/odb/pragma.cxx
@@ -0,0 +1,4442 @@
+// file : odb/pragma.cxx
+// 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 '()' and '{}' balance.
+ //
+ size_t p_balance (0), b_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_BRACE:
+ {
+ b_balance++;
+ break;
+ }
+ case CPP_CLOSE_BRACE:
+ {
+ b_balance--;
+ break;
+ }
+ case CPP_OPEN_PAREN:
+ {
+ p_balance++;
+ break;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ if (p_balance == 0 && b_balance == 0)
+ done = true;
+ else
+ p_balance--;
+ break;
+ }
+ case CPP_COMMA:
+ {
+ if (p_balance == 0 && b_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;
+ }
+}
+
+// Resolve a data member in the specified scope taking into account virtual
+// member declarations.
+//
+declaration
+resolve_data_member (tree scope,
+ const cxx_tokens& name,
+ string& decl_name, // Note: appended to.
+ string const& prag)
+{
+ declaration decl;
+
+ if (name.size () == 1 && name.back ().type == CPP_NAME)
+ {
+ virt_declarations::iterator i (virt_declarations_.find (scope));
+
+ if (i != virt_declarations_.end ())
+ {
+ string const& n (name.back ().literal);
+
+ virt_declaration_set::const_iterator j (i->second.find (n, FIELD_DECL));
+
+ if (j != i->second.end ())
+ {
+ decl = declaration (*j);
+ decl_name += n;
+ }
+ }
+ }
+
+ if (!decl)
+ {
+ cxx_tokens_lexer l;
+ l.start (name);
+
+ tree tn;
+ string tl;
+ cpp_ttype tt (l.next (tl));
+
+ tree d (resolve_scoped_name (
+ l, tt, tl, tn, scope, decl_name, false, prag));
+
+ if (d == 0)
+ return decl; // Diagnostics has already been issued.
+
+ decl = declaration (d);
+ }
+
+ return decl;
+}
+
+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 == "points_to" ||
+ 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 == "key_null" ||
+ p == "key_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>") | type(<name>)
+ // as("<subst>") | as(<name>)
+ // to("<subst>") | to(<expr>)
+ // from("<subst>") | from(<expr>)
+ //
+
+ if (p != "type" &&
+ p != "as" &&
+ p != "to" &&
+ p != "from")
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return;
+ }
+
+ // Make sure we've got the correct declaration type.
+ //
+ assert (decl == global_namespace);
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (qualifier_value.type_info () == typeid (custom_cxx_type))
+ {
+ // C++ type mapping.
+ //
+ custom_cxx_type& ct (qualifier_value.value<custom_cxx_type> ());
+
+ if (p == "type" || p == "as")
+ {
+ // Can be built-in type (e.g., bool).
+ //
+ if (tt == CPP_NAME || tt == CPP_KEYWORD || tt == CPP_SCOPE)
+ {
+ string name;
+ tree decl (
+ resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), name, true, p));
+
+ if (decl == 0)
+ return; // Diagnostics has already been issued.
+
+ if (TREE_CODE (decl) != TYPE_DECL)
+ {
+ error (loc) << "name '" << name << "' in db pragma "
+ << p << " does not refer to a type" << endl;
+ return;
+ }
+
+ (p == "type" ? ct.type_node : ct.as_node) = TREE_TYPE (decl);
+ (p == "type" ? ct.type_name : ct.as_name) = name;
+ }
+ else
+ {
+ error (l) << "type name expected in db pragma " << p << endl;
+ return;
+ }
+ }
+ else if (p == "to" || p == "from")
+ {
+ if (tt != CPP_CLOSE_PAREN) // Empty expression is ok.
+ {
+ if (!parse_expression (
+ l, tt, tl, tn, (p == "to" ? ct.to : ct.from), p))
+ return; // Diagnostics has already been issued.
+ }
+ }
+ }
+ else
+ {
+ using relational::custom_db_type;
+
+ // Database type mapping.
+ //
+ custom_db_type& ct (qualifier_value.value<custom_db_type> ());
+
+ 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;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+
+ 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 (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;
+ }
+
+ 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")
+ {
+ // id[(member-path)]
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ string name;
+
+ tt = l.next (tl, &tn);
+ if (tt == CPP_OPEN_PAREN)
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "data member name expected in db pragma " << p
+ << endl;
+ return;
+ }
+
+ name = tl;
+
+ for (tt = l.next (tl, &tn); 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;
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+
+ val = name;
+ }
+ 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 == "key_null" ||
+ p == "key_not_null" ||
+ p == "value_null" ||
+ p == "value_not_null")
+ {
+ // null
+ // not_null
+ // key_null
+ // key_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 through.
+ 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;
+ }
+
+ string 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;
+ }
+
+ name += '.';
+ name += tl;
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ val = name;
+ 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 == "points_to")
+ {
+ // points_to(<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);
+
+ if (tt == CPP_NAME || 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.
+
+ // Get the actual type if this is a TYPE_DECL. Also get the main
+ // variant.
+ //
+ if (TREE_CODE (type) == TYPE_DECL)
+ type = TREE_TYPE (type);
+
+ if (TYPE_P (type)) // Can be a template.
+ type = TYPE_MAIN_VARIANT (type);
+
+ if (TREE_CODE (type) != RECORD_TYPE)
+ {
+ error (loc) << "name '" << type_name << "' in db pragma " << p
+ << " does not refer to a class" << endl;
+ return;
+ }
+
+ val = type;
+ }
+ else
+ {
+ error (l) << "class 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);
+ }
+ 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 if (p == "before" || p == "after")
+ {
+ // Stray before/after specifier (i.e., without preceding virtual).
+ //
+ error (l) << p << " specifier must follow virtual" << 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 == "key-null" ||
+ name == "key-not-null" ||
+ 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 sequence 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>")]
+ // map type(<c++-type>) as(<c++-type>) [to(<expr>)] [from(<expr>)]
+ //
+
+ // The thing that determines whether this is a database or C++ type
+ // mapping is what we have inside 'type'. So to handle this we are
+ // going to pre-scan the pragma looking for 'type' and saving the
+ // tokens. Once we determine what this is, we replay the saved
+ // tokens to actually parse them.
+ //
+
+ // Determine what this is by scanning the pragma until we see
+ // the 'type' qualifier or EOF.
+ //
+ bool db (true);
+
+ bool done (false);
+ size_t balance (0);
+ for (tt = l.next (tl, &tn);
+ !(done && balance == 0) && tt != CPP_EOF;
+ tt = l.next (tl, &tn))
+ {
+ saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
+
+ switch (tt)
+ {
+ case CPP_OPEN_PAREN:
+ {
+ balance++;
+ continue;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ if (balance > 0)
+ balance--;
+ else
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+ continue;
+ }
+ case CPP_NAME:
+ {
+ if (balance == 0 && tl == "type")
+ break;
+
+ continue;
+ }
+ default:
+ continue;
+ }
+
+ tt = l.next (tl, &tn);
+ saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ balance++;
+
+ tt = l.next (tl, &tn);
+ saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
+
+ db = (tt == CPP_STRING);
+ }
+
+ done = true; // Scan until the closing ')'.
+ }
+
+ if (balance != 0)
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+
+ orig_decl = global_namespace;
+ decl = declaration (orig_decl);
+
+ if (db)
+ {
+ using relational::custom_db_type;
+
+ custom_db_type ct;
+ ct.loc = loc;
+ val = ct;
+ name = "custom-db-types";
+ adder = &accumulate<custom_db_type>;
+ }
+ else
+ {
+ custom_cxx_type ct;
+ ct.loc = loc;
+ ct.scope = current_scope ();
+ val = ct;
+ name = "custom-cxx-types";
+ adder = &accumulate<custom_cxx_type>;
+ }
+ }
+ 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;
+ location_t ord (loc);
+ int ord_bias (0);
+ {
+ 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);
+
+ // See if we have before/after specifiers.
+ //
+ if (tt == CPP_NAME && tl == "before")
+ {
+ // before[(<member>)]
+ //
+ // Before without the member name means first.
+ //
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "')' member name expected in db pragma before"
+ << endl;
+ }
+
+ string dn;
+ cxx_tokens ts (1, cxx_token (l.location (), CPP_NAME, tl));
+ declaration d (resolve_data_member (scope, ts, dn, "before"));
+
+ if (!d)
+ return; // Diagnostics has already been issued.
+
+ if (d.virt)
+ {
+ ord = d.decl.virt->ord;
+ ord_bias = d.decl.virt->ord_bias - 1;
+ }
+ else
+ {
+ ord = real_source_location (d.decl.real);
+ ord_bias = -1;
+ }
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma before"
+ << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else
+ ord = 0;
+ }
+
+ if (tt == CPP_NAME && tl == "after")
+ {
+ // after[(<member>)]
+ //
+ // Before without the member name means last.
+ //
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "')' member name expected in db pragma after"
+ << endl;
+ }
+
+ string dn;
+ cxx_tokens ts (1, cxx_token (l.location (), CPP_NAME, tl));
+ declaration d (resolve_data_member (scope, ts, dn, "after"));
+
+ if (!d)
+ return; // Diagnostics has already been issued.
+
+ if (d.virt)
+ {
+ ord = d.decl.virt->ord;
+ ord_bias = d.decl.virt->ord_bias + 1;
+ }
+ else
+ {
+ ord = real_source_location (d.decl.real);
+ ord_bias = 1;
+ }
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma after"
+ << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else
+ ord = ~location_t (0);
+ }
+ }
+
+ pair<virt_declaration_set::const_iterator, bool> r (
+ virt_declarations_[scope].insert (
+ virt_declaration (loc, ord, ord_bias, 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
+ {
+ // Not a virtual member declaration.
+ //
+ decl = resolve_data_member (scope, name_tokens, decl_name, p);
+
+ if (!decl)
+ return; // Diagnostics has already been issued.
+
+ // 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 == "key_null" ||
+ p == "key_not_null" ||
+ p == "value_null" ||
+ p == "value_not_null" ||
+ p == "default" ||
+ p == "section" ||
+ p == "load" ||
+ p == "update" ||
+ p == "inverse" ||
+ p == "on_delete" ||
+ p == "points_to" ||
+ 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_key_null (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "key_null");
+}
+
+extern "C" void
+handle_pragma_db_key_not_null (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "key_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_points_to (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "points_to");
+}
+
+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", "key_null", handle_pragma_db_key_null);
+ c_register_pragma_with_expansion ("db", "key_not_null", handle_pragma_db_key_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", "points_to", handle_pragma_db_points_to);
+ 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, view, 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, view, 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->second.context_name);
+
+ if (name == "object")
+ {
+ p = &j->second;
+ diag_name = "persistent object";
+ break;
+ }
+ else if (name == "view")
+ {
+ p = &j->second;
+ diag_name = "view";
+ break;
+ }
+ else if (name == "value")
+ {
+ p = &j->second;
+ diag_name = "composite value";
+ break;
+ }
+ // 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 ();
+ }
+ }
+}