aboutsummaryrefslogtreecommitdiff
path: root/odb
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2012-07-25 12:13:38 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2012-07-27 10:30:15 +0200
commit923639283d2bae0b82cb605fa4680a3058c9d973 (patch)
tree3c122b70129cdd2b502e1f7056eac896c109f03a /odb
parentadfa9bbd04cd3571932ee7675344ca723bfa1eab (diff)
Add support for defining indexes
New db pragma qualifier: index. New tests: common/index, mysql/index, pgsql/index.
Diffstat (limited to 'odb')
-rw-r--r--odb/cxx-lexer.cxx4
-rw-r--r--odb/cxx-lexer.hxx2
-rw-r--r--odb/diagnostics.hxx20
-rw-r--r--odb/location.cxx14
-rw-r--r--odb/location.hxx28
-rw-r--r--odb/lookup.cxx27
-rw-r--r--odb/lookup.hxx6
-rw-r--r--odb/makefile1
-rw-r--r--odb/pragma.cxx599
-rw-r--r--odb/relational/context.hxx35
-rw-r--r--odb/relational/model.cxx39
-rw-r--r--odb/relational/model.hxx216
-rw-r--r--odb/relational/mysql/schema.cxx30
-rw-r--r--odb/relational/pgsql/schema.cxx51
-rw-r--r--odb/relational/processor.cxx260
-rw-r--r--odb/relational/schema.hxx27
-rw-r--r--odb/relational/source.cxx2
-rw-r--r--odb/relational/validator.cxx118
-rw-r--r--odb/semantics/elements.cxx4
-rw-r--r--odb/semantics/elements.hxx19
-rw-r--r--odb/semantics/relational/index.hxx31
-rw-r--r--odb/semantics/relational/key.hxx9
-rw-r--r--odb/validator.cxx87
23 files changed, 1391 insertions, 238 deletions
diff --git a/odb/cxx-lexer.cxx b/odb/cxx-lexer.cxx
index b02b4e0..dc09a17 100644
--- a/odb/cxx-lexer.cxx
+++ b/odb/cxx-lexer.cxx
@@ -37,11 +37,11 @@ cxx_lexer::
//
void cxx_tokens_lexer::
-start (cxx_tokens const& ts)
+start (cxx_tokens const& ts, location_t start_loc)
{
tokens_ = &ts;
cur_ = ts.begin ();
- loc_ = 0;
+ loc_ = start_loc;
}
cpp_ttype cxx_tokens_lexer::
diff --git a/odb/cxx-lexer.hxx b/odb/cxx-lexer.hxx
index 9235ecf..2d0ca06 100644
--- a/odb/cxx-lexer.hxx
+++ b/odb/cxx-lexer.hxx
@@ -47,7 +47,7 @@ class cxx_tokens_lexer: public cxx_lexer
{
public:
void
- start (cxx_tokens const&);
+ start (cxx_tokens const&, location_t start_loc = 0);
virtual cpp_ttype
next (std::string& token, tree* node = 0);
diff --git a/odb/diagnostics.hxx b/odb/diagnostics.hxx
index a7dd575..b1e89b0 100644
--- a/odb/diagnostics.hxx
+++ b/odb/diagnostics.hxx
@@ -12,6 +12,8 @@
#include <cutl/fs/path.hxx>
+#include <odb/location.hxx>
+
using std::endl;
std::ostream&
@@ -23,6 +25,24 @@ warn (cutl::fs::path const&, std::size_t line, std::size_t clmn);
std::ostream&
info (cutl::fs::path const&, std::size_t line, std::size_t clmn);
+inline std::ostream&
+error (location const& l)
+{
+ return error (l.file, l.line, l.column);
+}
+
+inline std::ostream&
+warn (location const&l)
+{
+ return warn (l.file, l.line, l.column);
+}
+
+inline std::ostream&
+info (location const&l)
+{
+ return info (l.file, l.line, l.column);
+}
+
std::ostream&
error (location_t);
diff --git a/odb/location.cxx b/odb/location.cxx
new file mode 100644
index 0000000..ba8d9a7
--- /dev/null
+++ b/odb/location.cxx
@@ -0,0 +1,14 @@
+// file : odb/location.cxx
+// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/location.hxx>
+#include <odb/diagnostics.hxx>
+
+location::
+location (location_t l)
+ : file (location_file (l)),
+ line (location_line (l)),
+ column (location_column (l))
+{
+}
diff --git a/odb/location.hxx b/odb/location.hxx
new file mode 100644
index 0000000..a9cc6ad
--- /dev/null
+++ b/odb/location.hxx
@@ -0,0 +1,28 @@
+// file : odb/location.hxx
+// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_LOCATION_HXX
+#define ODB_LOCATION_HXX
+
+#include <odb/gcc-fwd.hxx>
+
+#include <cstddef>
+#include <cutl/fs/path.hxx>
+
+struct location
+{
+ explicit
+ location (location_t);
+
+ location (cutl::fs::path const& f, std::size_t l, std::size_t c)
+ : file (f), line (l), column (c)
+ {
+ }
+
+ cutl::fs::path file;
+ std::size_t line;
+ std::size_t column;
+};
+
+#endif // ODB_LOCATION_HXX
diff --git a/odb/lookup.cxx b/odb/lookup.cxx
index c6b40b1..b3e0551 100644
--- a/odb/lookup.cxx
+++ b/odb/lookup.cxx
@@ -48,6 +48,7 @@ namespace lookup
tree scope,
string& name,
bool is_type,
+ bool trailing_scope,
tree* end_scope)
{
tree id;
@@ -78,7 +79,24 @@ namespace lookup
ptt = tt;
tt = l.next (tl, &tn);
- bool last (tt != CPP_SCOPE);
+ bool last (true);
+ if (tt == CPP_SCOPE)
+ {
+ // If trailing scope names are allowed, then we also need to
+ // check what's after the scope.
+ //
+ if (trailing_scope)
+ {
+ ptt = tt;
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_NAME)
+ last = false;
+ }
+ else
+ last = false;
+ }
+
tree decl = lookup_qualified_name (scope, id, last && is_type, false);
// If this is the first component in the name, then also search the
@@ -110,8 +128,11 @@ namespace lookup
name += "::";
- ptt = tt;
- tt = l.next (tl, &tn);
+ if (!trailing_scope)
+ {
+ ptt = tt;
+ tt = l.next (tl, &tn);
+ }
}
return scope;
diff --git a/odb/lookup.hxx b/odb/lookup.hxx
index 7df8f34..c207543 100644
--- a/odb/lookup.hxx
+++ b/odb/lookup.hxx
@@ -40,6 +40,11 @@ namespace lookup
std::string& tl, // Token literal.
tree& tn); // Token node.
+ // If trailing_scope is true, then this function also handles
+ // names in the 'foo::bar::<something-other-than-name>' form.
+ // In this case token will be <something-other-than-name> and
+ // ptt will be CPP_SCOPE.
+ //
tree
resolve_scoped_name (cxx_lexer&,
cpp_ttype&,
@@ -49,6 +54,7 @@ namespace lookup
tree start_scope,
std::string& name,
bool is_type,
+ bool trailing_scope = false,
tree* end_scope = 0);
}
diff --git a/odb/makefile b/odb/makefile
index d906fa0..c57ed9f 100644
--- a/odb/makefile
+++ b/odb/makefile
@@ -12,6 +12,7 @@ sql-token.cxx \
sql-lexer.cxx \
context.cxx \
common.cxx \
+location.cxx \
diagnostics.cxx \
emitter.cxx \
lookup.cxx \
diff --git a/odb/pragma.cxx b/odb/pragma.cxx
index 0d21f7b..abb6692 100644
--- a/odb/pragma.cxx
+++ b/odb/pragma.cxx
@@ -319,14 +319,21 @@ resolve_scoped_name (cxx_lexer& l,
tree& tn,
string& name,
bool is_type,
- string const& prag)
+ string const& prag,
+ bool trailing_scope = false,
+ cpp_ttype* prev_tt = 0)
{
try
{
cpp_ttype ptt; // Not used.
- return
+ tree r (
lookup::resolve_scoped_name (
- l, tt, tl, tn, ptt, current_scope (), name, is_type);
+ l, tt, tl, tn, ptt, current_scope (), name, is_type, trailing_scope));
+
+ if (prev_tt != 0)
+ *prev_tt = ptt;
+
+ return r;
}
catch (lookup::invalid_name const&)
{
@@ -365,11 +372,13 @@ check_spec_decl_type (tree d,
return false;
}
}
- else if (p == "auto" ||
- p == "column" ||
- p == "inverse" ||
+ else if (p == "auto" ||
+ p == "column" ||
+ p == "inverse" ||
p == "transient" ||
- p == "version")
+ p == "version" ||
+ p == "index" ||
+ p == "unique")
{
if (tc != FIELD_DECL)
{
@@ -580,7 +589,278 @@ handle_pragma (cxx_lexer& l,
pragma::add_func adder (0); // Custom context adder.
location_t loc (l.location ()); // Pragma location.
- if (p == "table")
+ if (qualifier == "map")
+ {
+ // type("<regex>")
+ // as("<subst>")
+ // to("<subst>")
+ // from("<subst>")
+ //
+
+ if (p != "type" &&
+ p != "as" &&
+ p != "to" &&
+ p != "from")
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return;
+ }
+
+ using relational::custom_db_type;
+
+ // Make sure we've got the correct declaration type.
+ //
+ assert (decl == global_namespace);
+ custom_db_type& ct (qualifier_value.value<custom_db_type> ());
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (p == "type")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "type name regex expected in db pragma " << p << endl;
+ return;
+ }
+
+ try
+ {
+ // Make it case-insensitive.
+ //
+ ct.type.assign (tl, true);
+ }
+ catch (regex_format const& e)
+ {
+ error (l) << "invalid regex: '" << e.regex () << "' in db pragma "
+ << p << ": " << e.description () << endl;
+ return;
+ }
+ }
+ else if (p == "as")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "type name expected in db pragma " << p << endl;
+ return;
+ }
+
+ ct.as = tl;
+ }
+ else if (p == "to")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "expression expected in db pragma " << p << endl;
+ return;
+ }
+
+ ct.to = tl;
+ }
+ else if (p == "from")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "expression expected in db pragma " << p << endl;
+ return;
+ }
+
+ ct.from = tl;
+ }
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ name.clear (); // We don't need to add anything for this pragma.
+ tt = l.next (tl, &tn);
+ }
+ else if (qualifier == "index")
+ {
+ // unique
+ // type("<type>")
+ // method("<method>")
+ // options("<options>")
+ // member(<name>[, "<options>"])
+ // members(<name>[, <name>...])
+ //
+
+ if (p != "unique" &&
+ p != "type" &&
+ p != "method" &&
+ p != "options" &&
+ p != "member" &&
+ p != "members")
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return;
+ }
+
+ using relational::index;
+ index& in (qualifier_value.value<index> ());
+
+ if (p == "unique")
+ in.type = "UNIQUE";
+ else
+ {
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (p == "type")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "index type expected in db pragma " << p << endl;
+ return;
+ }
+
+ in.type = tl;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "method")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "index method expected in db pragma " << p << endl;
+ return;
+ }
+
+ in.method = tl;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "options")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "index options expected in db pragma " << p << endl;
+ return;
+ }
+
+ in.options = tl;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "member")
+ {
+ if (tt != CPP_NAME)
+ {
+ error (l) << "data member name expected in db pragma " << p << endl;
+ return;
+ }
+
+ index::member m;
+ m.loc = loc;
+ m.name = tl;
+
+ tt = l.next (tl, &tn);
+
+ // Parse nested members if any.
+ //
+ for (; tt == CPP_DOT; tt = l.next (tl, &tn))
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "name expected after '.' in db pragma " << p << endl;
+ return;
+ }
+
+ m.name += '.';
+ m.name += tl;
+ }
+
+ // Parse member options, if any.
+ //
+ if (tt == CPP_COMMA)
+ {
+ if (l.next (tl, &tn) != CPP_STRING)
+ {
+ error (l) << "index member options expected in db pragma " << p
+ << endl;
+ return;
+ }
+
+ m.options = tl;
+ tt = l.next (tl, &tn);
+ }
+
+ in.members.push_back (m);
+ }
+ else if (p == "members")
+ {
+ for (;;)
+ {
+ if (tt != CPP_NAME)
+ {
+ error (l) << "data member name expected in db pragma " << p
+ << endl;
+ return;
+ }
+
+ index::member m;
+ m.loc = l.location ();
+ m.name = tl;
+
+ tt = l.next (tl, &tn);
+
+ // Parse nested members if any.
+ //
+ for (; tt == CPP_DOT; tt = l.next (tl, &tn))
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "name expected after '.' in db pragma " << p
+ << endl;
+ return;
+ }
+
+ m.name += '.';
+ m.name += tl;
+ }
+
+ in.members.push_back (m);
+
+ if (tt == CPP_COMMA)
+ tt = l.next (tl, &tn);
+ else
+ break;
+ }
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+ }
+
+ name.clear (); // We don't need to add anything for this pragma.
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "index" ||
+ p == "unique")
+ {
+ // index
+ // unique
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl != 0 && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "table")
{
// table (<name>)
// table (<name> [= "<alias>"] [: "<cond>"] (view only)
@@ -1358,93 +1638,6 @@ handle_pragma (cxx_lexer& l,
adder = &accumulate<string>;
tt = l.next (tl, &tn);
}
- else if (qualifier == "map" &&
- (p == "type" ||
- p == "as" ||
- p == "to" ||
- p == "from"))
- {
- // type("<regex>")
- // as("<subst>")
- // to("<subst>")
- // from("<subst>")
- //
- using relational::custom_db_type;
-
- // Make sure we've got the correct declaration type.
- //
- assert (decl == global_namespace);
- custom_db_type& ct (qualifier_value.value<custom_db_type> ());
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- if (p == "type")
- {
- if (tt != CPP_STRING)
- {
- error (l) << "type name regex expected in db pragma " << p << endl;
- return;
- }
-
- try
- {
- // Make it case-insensitive.
- //
- ct.type.assign (tl, true);
- }
- catch (regex_format const& e)
- {
- error (l) << "invalid regex: '" << e.regex () << "' in db pragma "
- << p << ": " << e.description () << endl;
- return;
- }
- }
- else if (p == "as")
- {
- if (tt != CPP_STRING)
- {
- error (l) << "type name expected in db pragma " << p << endl;
- return;
- }
-
- ct.as = tl;
- }
- else if (p == "to")
- {
- if (tt != CPP_STRING)
- {
- error (l) << "expression expected in db pragma " << p << endl;
- return;
- }
-
- ct.to = tl;
- }
- else if (p == "from")
- {
- if (tt != CPP_STRING)
- {
- error (l) << "expression expected in db pragma " << p << endl;
- return;
- }
-
- ct.from = tl;
- }
-
- if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- name.clear (); // We don't need to add anything for this pragma.
- tt = l.next (tl, &tn);
- }
else if (p == "type" ||
p == "id_type" ||
p == "value_type" ||
@@ -1777,6 +1970,26 @@ check_qual_decl_type (tree d,
{
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)
@@ -1856,6 +2069,8 @@ handle_pragma_qualifier (cxx_lexer& l, string const& p)
pragma::add_func adder (0); // Custom context adder.
bool ns (false); // Namespace location pragma.
+ cxx_tokens saved_tokens; // Saved token seuqnece to be replayed.
+
// Pragma qualifiers.
//
if (p == "map")
@@ -1872,6 +2087,175 @@ handle_pragma_qualifier (cxx_lexer& l, string const& p)
adder = &accumulate<custom_db_type>;
tt = l.next (tl, &tn);
}
+ else if (p == "index")
+ {
+ // Index can be both a qualifier and a specifier. Things are complicated
+ // by the fact that when it is a specifier, it belongs to a member which
+ // means that the actual qualifier ('member') can be omitted. So we need
+ // to distinguish between cases like these:
+ //
+ // #pragma db index type("INTEGER") // specifier
+ // #pragma db index type("UNIQUE") member(foo_) // qualifier
+ //
+ // The thing that determines whether this is a qualifier or a specifier
+ // is the presence of the 'member' or 'members' specifier. So to handle
+ // this we are going to pre-scan the pragma looking for 'member' or
+ // 'members' and saving the tokens. Once we determine what this is,
+ // we replay the saved tokens to actually parse them.
+ //
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_OPEN_PAREN)
+ {
+ // Determine what this is by scanning the pragma until we see
+ // the 'member' qualifier or EOF.
+ //
+ bool qual (false);
+ size_t balance (0);
+
+ for (; tt != CPP_EOF; tt = l.next (tl, &tn))
+ {
+ switch (tt)
+ {
+ case CPP_OPEN_PAREN:
+ {
+ balance++;
+ break;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ if (balance > 0)
+ balance--;
+ else
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+ break;
+ }
+ case CPP_NAME:
+ {
+ if (balance == 0 && (tl == "member" || tl == "members"))
+ qual = true;
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (qual)
+ break;
+
+ cxx_token ct (l.location (), tt);
+ ct.literal = tl;
+ ct.node = tn;
+ saved_tokens.push_back (ct);
+ }
+
+ if (balance != 0)
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+
+ if (qual)
+ {
+ // This is a qualifer. The saved tokens sequence contains tokens
+ // until the first 'member' or 'members' specifier. So we will
+ // first need to re-play these tokens and then continue parsing
+ // as if we just saw the 'member' or 'members' specifier. The
+ // token type (tt) and token literal (tl) variables should contain
+ // the correct values.
+ //
+ orig_decl = decl = current_scope ();
+ }
+ else
+ {
+ // This is a specifier. The saved tokens sequence contains all the
+ // tokens in this pragma until EOF.
+ //
+ cxx_tokens_lexer l;
+ l.start (saved_tokens, loc);
+ handle_pragma (l, "index", "member", val, 0, "", 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, 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 = TYPE_MAIN_VARIANT (orig_decl);
+ else
+ decl = 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 = decl = current_scope ();
+
+ // 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>)]
@@ -2032,6 +2416,7 @@ handle_pragma_qualifier (cxx_lexer& l, string const& p)
//
else if (p == "id" ||
p == "auto" ||
+ p == "unique" ||
p == "column" ||
p == "value_column" ||
p == "index_column" ||
@@ -2104,8 +2489,30 @@ handle_pragma_qualifier (cxx_lexer& l, string const& p)
}
}
- // See if there are any more pragmas.
+ // See if there are any saved tokens to replay.
//
+ if (!saved_tokens.empty ())
+ {
+ cxx_tokens_lexer l;
+ l.start (saved_tokens);
+
+ string tl;
+ cpp_ttype tt (l.next (tl));
+
+ if (tt == CPP_NAME || tt == CPP_KEYWORD)
+ {
+ handle_pragma (l, tl, p, *pval, decl, decl_name, ns);
+
+ if (errorcount != 0) // Avoid parsing the rest if there was an error.
+ return;
+ }
+ else if (tt != CPP_EOF)
+ {
+ error (l) << "unexpected text after " << p << " in db pragma" << endl;
+ return;
+ }
+ }
+
if (tt == CPP_NAME || tt == CPP_KEYWORD)
{
handle_pragma (l, tl, p, *pval, decl, decl_name, ns);
diff --git a/odb/relational/context.hxx b/odb/relational/context.hxx
index ca55650..990e0aa 100644
--- a/odb/relational/context.hxx
+++ b/odb/relational/context.hxx
@@ -23,6 +23,41 @@ namespace relational
statement_where // WHERE clause.
};
+ // Index.
+ //
+ struct index
+ {
+ location_t loc; // Location of this index definition.
+ std::string name; // If empty, then derive from the member name.
+ std::string type; // E.g., "UNIQUE", etc.
+ std::string method; // E.g., "BTREE", etc.
+ std::string options; // Database-specific index options.
+
+ struct member
+ {
+ location_t loc; // Location of this member specifier.
+ std::string name; // Member name, e.g., foo_, foo_.bar_.
+ data_member_path path; // Member path.
+ std::string options; // Member options, e.g., "ASC", etc.
+ };
+ typedef std::vector<member> members_type;
+
+ members_type members;
+ };
+
+ typedef std::vector<index> indexes;
+
+ // Indexes in the above vector are in location order.
+ //
+ struct index_comparator
+ {
+ bool
+ operator() (index const& x, index const& y) const
+ {
+ return x.loc < y.loc;
+ }
+ };
+
// Custom database type mapping.
//
struct custom_db_type
diff --git a/odb/relational/model.cxx b/odb/relational/model.cxx
index bd91b3c..48c9bd2 100644
--- a/odb/relational/model.cxx
+++ b/odb/relational/model.cxx
@@ -8,6 +8,8 @@
#include <limits>
#include <sstream>
+#include <odb/diagnostics.hxx>
+
#include <odb/relational/model.hxx>
#include <odb/relational/generate.hxx>
@@ -143,24 +145,25 @@ namespace relational
}
catch (sema_rel::duplicate_name const& e)
{
- semantics::node& o (*e.orig.get<semantics::node*> ("cxx-node"));
- semantics::node& d (*e.dup.get<semantics::node*> ("cxx-node"));
-
- cerr << d.file () << ":" << d.line () << ":" << d.column ()
- << ": error: " << e.dup.kind () << " name '" << e.name
- << "' conflicts with an already defined " << e.orig.kind ()
- << " name"
- << endl;
-
- cerr << o.file () << ":" << o.line () << ":" << o.column ()
- << ": info: conflicting " << e.orig.kind () << " is "
- << "defined here"
- << endl;
-
- cerr << d.file () << ":" << d.line () << ":" << d.column ()
- << ": error: use '#pragma db column' or '#pragma db table' "
- << "to change one of the names"
- << endl;
+ location const& o (e.orig.get<location> ("cxx-location"));
+ location const& d (e.dup.get<location> ("cxx-location"));
+
+ error (d) << e.dup.kind () << " name '" << e.name << "' conflicts "
+ << "with an already defined " << e.orig.kind () << " name"
+ << endl;
+
+ info (o) << "conflicting " << e.orig.kind () << " is defined here"
+ << endl;
+
+ if (e.dup.kind () == "index")
+ error (d) << "use #pragma db index to change one of the names"
+ << endl;
+ else if (e.dup.kind () == "table")
+ error (d) << "use #pragma db table to change one of the names"
+ << endl;
+ else
+ error (d) << "use #pragma db column to change its name"
+ << endl;
throw operation_failed ();
}
diff --git a/odb/relational/model.hxx b/odb/relational/model.hxx
index b95425e..6ffffca 100644
--- a/odb/relational/model.hxx
+++ b/odb/relational/model.hxx
@@ -109,9 +109,7 @@ namespace relational
sema_rel::column& c (
model_.new_node<sema_rel::column> (col_id, column_type (), null));
-
- c.set ("cxx-node", static_cast<semantics::node*> (&m));
-
+ c.set ("cxx-location", m.location ());
model_.new_edge<sema_rel::unames> (table_, c, name);
// An id member cannot have a default value.
@@ -196,7 +194,7 @@ namespace relational
{
pkey_ = &model_.new_node<sema_rel::primary_key> (
m.count ("auto"));
- pkey_->set ("cxx-node", static_cast<semantics::node*> (idm));
+ pkey_->set ("cxx-location", idm->location ());
// In most databases the primary key constraint can be
// manipulated without an explicit name. So we use the special
@@ -236,7 +234,7 @@ namespace relational
model_.new_node<foreign_key> (
id, table_name (c), deferred, on_delete));
- fk.set ("cxx-node", static_cast<semantics::node*> (&m));
+ fk.set ("cxx-location", m.location ());
bool simple;
@@ -328,6 +326,93 @@ namespace relational
bool id_override_;
};
+ struct object_indexes: traversal::class_, virtual context
+ {
+ typedef object_indexes base;
+
+ object_indexes (sema_rel::model& model, sema_rel::table& table)
+ : model_ (model), table_ (table)
+ {
+ *this >> inherits_ >> *this;
+ }
+
+ object_indexes (object_indexes const& x)
+ : root_context (), context (), //@@ -Wextra
+ model_ (x.model_), table_ (x.table_)
+ {
+ *this >> inherits_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ if (!object (c)) // Ignore transient bases.
+ return;
+
+ // Polymorphic bases get their own tables.
+ //
+ if (!polymorphic (c))
+ inherits (c);
+
+ indexes& ins (c.get<indexes> ("index"));
+
+ for (indexes::iterator i (ins.begin ()); i != ins.end (); ++i)
+ {
+ // Using index name as its id.
+ //
+ sema_rel::index& in (
+ model_.new_node<sema_rel::index> (
+ i->name, i->type, i->method, i->options));
+ in.set ("cxx-location", location (i->loc));
+ model_.new_edge<sema_rel::unames> (table_, in, i->name);
+
+ for (index::members_type::iterator j (i->members.begin ());
+ j != i->members.end (); ++j)
+ {
+ using sema_rel::column;
+
+ index::member& im (*j);
+
+ if (type* comp = composite_wrapper (utype (*im.path.back ())))
+ {
+ // Composite value. Get the list of the columns. Here
+ // column_name() returns the column prefix.
+ //
+ instance<object_columns_list> ocl (column_name (im.path));
+ ocl->traverse (*comp);
+
+ for (object_columns_list::iterator i (ocl->begin ());
+ i != ocl->end (); ++i)
+ {
+ column& c (
+ dynamic_cast<column&> (
+ table_.find (i->name)->nameable ()));
+
+ model_.new_edge<sema_rel::contains> (in, c, im.options);
+ }
+ }
+ else
+ {
+ // Simple value. Get the column name and look it up in the
+ // table.
+ //
+ column& c (
+ dynamic_cast<column&> (
+ table_.find (column_name (im.path))->nameable ()));
+
+ model_.new_edge<sema_rel::contains> (in, c, im.options);
+ }
+ }
+ }
+ }
+
+ private:
+ sema_rel::model& model_;
+ sema_rel::table& table_;
+
+ traversal::inherits inherits_;
+ };
+
struct member_create: object_members_base, virtual context
{
typedef member_create base;
@@ -384,7 +469,6 @@ namespace relational
using semantics::type;
using semantics::data_member;
- using sema_rel::index;
using sema_rel::column;
// Ignore inverse containers of object pointers.
@@ -404,8 +488,7 @@ namespace relational
sema_rel::container_table& t (
model_.new_node<sema_rel::container_table> (id));
- t.set ("cxx-node", static_cast<semantics::node*> (&m));
-
+ t.set ("cxx-location", m.location ());
model_.new_edge<sema_rel::qnames> (model_, t, name);
// object_id
@@ -418,16 +501,31 @@ namespace relational
// Foreign key and index for the object id.
//
{
+ // Derive the name prefix. See the comment for the other foreign
+ // key code above.
+ //
+ // Note also that id_name can be a column prefix (if id is
+ // composite), in which case it can be empty and if not, then
+ // it will most likely already contain a trailing underscore.
+ //
+ string id_name (column_name (m, "id", "object_id"));
+
+ if (id_name.empty ())
+ id_name = "object_id";
+
+ if (id_name[id_name.size () - 1] != '_')
+ id_name += '_';
+
+ // Foreign key.
+ //
sema_rel::foreign_key& fk (
model_.new_node<sema_rel::foreign_key> (
id + ".id",
table_name (*context::top_object),
false, // immediate
sema_rel::foreign_key::cascade));
- fk.set ("cxx-node", static_cast<semantics::node*> (&m));
-
- index& in (model_.new_node<index> (id + ".id"));
- in.set ("cxx-node", static_cast<semantics::node*> (&m));
+ fk.set ("cxx-location", m.location ());
+ model_.new_edge<sema_rel::unames> (t, fk, id_name + "fk");
// Get referenced columns.
//
@@ -443,35 +541,47 @@ namespace relational
}
// All the columns we have in this table so far are for the
- // object id. Add them to the foreign key and the index.
+ // object id. Add them to the foreign key.
//
for (sema_rel::table::names_iterator i (t.names_begin ());
i != t.names_end ();
++i)
{
- column& c (dynamic_cast<column&> (i->nameable ()));
-
- model_.new_edge<sema_rel::contains> (fk, c);
- model_.new_edge<sema_rel::contains> (in, c);
+ if (column* c = dynamic_cast<column*> (&i->nameable ()))
+ model_.new_edge<sema_rel::contains> (fk, *c);
}
- // Derive the names. See the comment for the other foreign key
- // code above.
- //
- // Note also that id_name can be a column prefix (if id is
- // composite), in which case it can be empty and if not, then
- // it will most likely already contain a trailing underscore.
+ // Index. See if we have a custom index.
//
- string id_name (column_name (m, "id", "object_id"));
-
- if (id_name.empty ())
- id_name = "object_id";
+ index* sin (m.count ("id-index") ? &m.get<index> ("id-index") : 0);
+ sema_rel::index* in (0);
- if (id_name[id_name.size () - 1] != '_')
- id_name += '_';
+ if (sin != 0)
+ {
+ in = &model_.new_node<sema_rel::index> (
+ id + ".id", sin->type, sin->method, sin->options);
+ in->set ("cxx-location", sin->loc);
+ model_.new_edge<sema_rel::unames> (
+ t, *in, (sin->name.empty () ? id_name + "i" : sin->name));
+ }
+ else
+ {
+ in = &model_.new_node<sema_rel::index> (id + ".id");
+ in->set ("cxx-location", m.location ());
+ model_.new_edge<sema_rel::unames> (t, *in, id_name + "i");
+ }
- model_.new_edge<sema_rel::unames> (t, fk, id_name + "fk");
- model_.new_edge<sema_rel::unames> (t, in, id_name + "i");
+ // All the columns we have in this table so far are for the
+ // object id. Add them to the index.
+ //
+ for (sema_rel::table::names_iterator i (t.names_begin ());
+ i != t.names_end ();
+ ++i)
+ {
+ if (column* c = dynamic_cast<column*> (&i->nameable ()))
+ model_.new_edge<sema_rel::contains> (
+ *in, *c, (sin != 0 ? sin->members.back ().options : ""));
+ }
}
// index (simple value)
@@ -479,18 +589,39 @@ namespace relational
bool ordered (ck == ck_ordered && !unordered (m));
if (ordered)
{
+ // Column.
+ //
instance<object_columns> oc (model_, t);
oc->traverse (m, container_it (ct), "index", "index");
string col_name (column_name (m, "index", "index"));
- index& in (model_.new_node<index> (id + ".index"));
- in.set ("cxx-node", static_cast<semantics::node*> (&m));
+ // Index. See if we have a custom index.
+ //
+ index* sin (m.count ("index-index")
+ ? &m.get<index> ("index-index")
+ : 0);
+ sema_rel::index* in (0);
- model_.new_edge<sema_rel::contains> (
- in, dynamic_cast<column&> (t.find (col_name)->nameable ()));
+ if (sin != 0)
+ {
+ in = &model_.new_node<sema_rel::index> (
+ id + ".index", sin->type, sin->method, sin->options);
+ in->set ("cxx-location", sin->loc);
+ model_.new_edge<sema_rel::unames> (
+ t, *in, (sin->name.empty () ? col_name + "_i" : sin->name));
+ }
+ else
+ {
+ in = &model_.new_node<sema_rel::index> (id + ".index");
+ in->set ("cxx-location", m.location ());
+ model_.new_edge<sema_rel::unames> (t, *in, col_name + "_i");
+ }
- model_.new_edge<sema_rel::unames> (t, in, col_name + "_i");
+ model_.new_edge<sema_rel::contains> (
+ *in,
+ dynamic_cast<column&> (t.find (col_name)->nameable ()),
+ (sin != 0 ? sin->members.back ().options : ""));
}
// key
@@ -551,18 +682,25 @@ namespace relational
sema_rel::object_table& t(
model_.new_node<sema_rel::object_table> (id));
-
- t.set ("cxx-node", static_cast<semantics::node*> (&c));
-
+ t.set ("cxx-location", c.location ());
model_.new_edge<sema_rel::qnames> (model_, t, name);
sema_rel::model::names_iterator begin (--model_.names_end ());
+ // Add columns.
+ //
{
instance<object_columns> oc (model_, t);
oc->traverse (c);
}
+ // Add indexes.
+ //
+ {
+ instance<object_indexes> oi (model_, t);
+ oi->traverse (c);
+ }
+
tables_.insert (name);
// Create tables for members.
diff --git a/odb/relational/mysql/schema.cxx b/odb/relational/mysql/schema.cxx
index 4050d70..8068fc4 100644
--- a/odb/relational/mysql/schema.cxx
+++ b/odb/relational/mysql/schema.cxx
@@ -125,6 +125,36 @@ namespace relational
}
};
entry<create_table> create_table_;
+
+ struct create_index: relational::create_index, context
+ {
+ create_index (base const& x): base (x) {}
+
+ virtual void
+ create (sema_rel::index& in)
+ {
+ os << "CREATE ";
+
+ if (!in.type ().empty ())
+ os << in.type () << ' ';
+
+ os << "INDEX " << name (in);
+
+ if (!in.method ().empty ())
+ os << " USING " << in.method ();
+
+ os << endl
+ << " ON " << table_name (in) << " (";
+
+ columns (in);
+
+ os << ")" << endl;
+
+ if (!in.options ().empty ())
+ os << ' ' << in.options () << endl;
+ }
+ };
+ entry<create_index> create_index_;
}
}
}
diff --git a/odb/relational/pgsql/schema.cxx b/odb/relational/pgsql/schema.cxx
index 2113894..5574bdb 100644
--- a/odb/relational/pgsql/schema.cxx
+++ b/odb/relational/pgsql/schema.cxx
@@ -4,6 +4,7 @@
#include <set>
+#include <odb/diagnostics.hxx>
#include <odb/relational/schema.hxx>
#include <odb/relational/pgsql/common.hxx>
@@ -74,11 +75,10 @@ namespace relational
os << "BIGSERIAL";
else
{
- semantics::node& n (*c.get<semantics::node*> ("cxx-node"));
+ location const& l (c.get<location> ("cxx-location"));
- cerr << n.file () << ":" << n.line () << ":" << n.column ()
- << ": error: automatically assigned object id must map "
- << "to PostgreSQL INTEGER or BIGINT" << endl;
+ error (l) << "automatically assigned object id must map "
+ << "to PostgreSQL INTEGER or BIGINT" << endl;
throw operation_failed ();
}
@@ -181,6 +181,49 @@ namespace relational
static_cast<sema_rel::table&> (in.scope ()).name ().uname ()
+ "_" + in.name ());
}
+
+ virtual void
+ create (sema_rel::index& in)
+ {
+ os << "CREATE ";
+
+ if (!in.type ().empty ())
+ {
+ // Handle the CONCURRENTLY keyword.
+ //
+ string const& t (in.type ());
+
+ if (t == "concurrently" || t == "CONCURRENTLY")
+ {
+ os << "INDEX " << t;
+ }
+ else
+ {
+ size_t p (t.rfind (' '));
+ string s (t, (p != string::npos ? p + 1 : 0), string::npos);
+
+ if (s == "concurrently" || s == "CONCURRENTLY")
+ os << string (t, 0, p) << " INDEX " << s;
+ else
+ os << t << " INDEX";
+ }
+ }
+ else
+ os << "INDEX";
+
+ os << " " << name (in) << endl
+ << " ON " << table_name (in);
+
+ if (!in.method ().empty ())
+ os << " USING " << in.method ();
+
+ os << " (";
+ columns (in);
+ os << ")" << endl;
+
+ if (!in.options ().empty ())
+ os << ' ' << in.options () << endl;
+ }
};
entry<create_index> create_index_;
}
diff --git a/odb/relational/processor.cxx b/odb/relational/processor.cxx
index 5cb5f35..f496a0d 100644
--- a/odb/relational/processor.cxx
+++ b/odb/relational/processor.cxx
@@ -5,6 +5,7 @@
#include <odb/gcc.hxx>
#include <vector>
+#include <algorithm>
#include <odb/diagnostics.hxx>
#include <odb/lookup.hxx>
@@ -119,6 +120,8 @@ namespace relational
if (transient (m))
return;
+ process_index (m);
+
semantics::names* hint;
semantics::type& t (utype (m, hint));
@@ -290,6 +293,44 @@ namespace relational
throw operation_failed ();
}
+ // Convert index/unique specifiers to the index entry in the object.
+ //
+ void
+ process_index (semantics::data_member& m)
+ {
+ bool ip (m.count ("index"));
+ bool up (m.count ("unique"));
+
+ if (ip || up)
+ {
+ using semantics::class_;
+ class_& c (dynamic_cast<class_&> (m.scope ()));
+
+ indexes& ins (c.count ("index")
+ ? c.get<indexes> ("index")
+ : c.set ("index", indexes ()));
+
+ index in;
+ in.loc = m.get<location_t> (
+ ip ? "index-location" : "unique-location");
+
+ if (up)
+ in.type = "UNIQUE";
+
+ index::member im;
+ im.loc = in.loc;
+ im.name = m.name ();
+ im.path.push_back (&m);
+ in.members.push_back (im);
+
+ // Insert it in the location order.
+ //
+ ins.insert (
+ lower_bound (ins.begin (), ins.end (), in, index_comparator ()),
+ in);
+ }
+ }
+
void
process_container_value (semantics::type& t,
semantics::names* hint,
@@ -1323,7 +1364,7 @@ namespace relational
tree type;
cpp_ttype ptt; // Not used.
decl = lookup::resolve_scoped_name (
- lex_, tt, tl, tn, ptt, i->scope, name, false, &type);
+ lex_, tt, tl, tn, ptt, i->scope, name, false, false, &type);
type = TYPE_MAIN_VARIANT (type);
@@ -1801,16 +1842,18 @@ namespace relational
if (k == class_object || k == class_view)
assign_pointer (c);
- // Do some additional pre-processing for objects.
+ // Do some additional pre-processing.
//
if (k == class_object)
traverse_object_pre (c);
names (c);
- // Do some additional post-processing for views.
+ // Do some additional post-processing.
//
- if (k == class_view)
+ if (k == class_object)
+ traverse_object_post (c);
+ else if (k == class_view)
traverse_view_post (c);
}
@@ -1957,6 +2000,191 @@ namespace relational
}
}
+ virtual void
+ traverse_object_post (type& c)
+ {
+ // Process indexes. Here we need to do two things: resolve member
+ // names to member paths and assign names to unnamed indexes. We
+ // are also going to handle the special container indexes.
+ //
+ indexes& ins (c.count ("index")
+ ? c.get<indexes> ("index")
+ : c.set ("index", indexes ()));
+
+ for (indexes::iterator i (ins.begin ()); i != ins.end ();)
+ {
+ index& in (*i);
+
+ // This should never happen since a db index pragma without
+ // the member specifier will be treated as a member pragma.
+ //
+ assert (!in.members.empty ());
+
+ // First resolve member names.
+ //
+ string tl;
+ tree tn;
+ cpp_ttype tt;
+
+ index::members_type::iterator j (in.members.begin ());
+ for (; j != in.members.end (); ++j)
+ {
+ index::member& im (*j);
+
+ if (!im.path.empty ())
+ continue; // Already resolved.
+
+ try
+ {
+ lex_.start (im.name);
+ tt = lex_.next (tl, &tn);
+
+ // The name was already verified to be syntactically correct so
+ // we don't need to do any error checking.
+ //
+ string name; // Not used.
+ cpp_ttype ptt; // Not used.
+ tree decl (
+ lookup::resolve_scoped_name (
+ lex_, tt, tl, tn, ptt, c.tree_node (), name, false));
+
+ // Check that we have a data member.
+ //
+ if (TREE_CODE (decl) != FIELD_DECL)
+ {
+ error (im.loc) << "name '" << tl << "' in db pragma member "
+ << "does not refer to a data member" << endl;
+ throw operation_failed ();
+ }
+
+ using semantics::data_member;
+
+ data_member* m (dynamic_cast<data_member*> (unit.find (decl)));
+ im.path.push_back (m);
+
+ if (container (*m))
+ break;
+
+ // Resolve nested members if any.
+ //
+ for (; tt == CPP_DOT; tt = lex_.next (tl, &tn))
+ {
+ lex_.next (tl, &tn); // Get CPP_NAME.
+
+ tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
+
+ decl = lookup_qualified_name (
+ type, get_identifier (tl.c_str ()), false, false);
+
+ if (decl == error_mark_node || TREE_CODE (decl) != FIELD_DECL)
+ {
+ error (im.loc) << "name '" << tl << "' in db pragma member "
+ << "does not refer to a data member" << endl;
+ throw operation_failed ();
+ }
+
+ m = dynamic_cast<data_member*> (unit.find (decl));
+ im.path.push_back (m);
+
+ if (container (*m))
+ {
+ tt = lex_.next (tl, &tn); // Get CPP_DOT.
+ break; // Only breaks out of the inner loop.
+ }
+ }
+
+ if (container (*m))
+ break;
+ }
+ catch (lookup::invalid_name const&)
+ {
+ error (im.loc) << "invalid name in db pragma member" << endl;
+ throw operation_failed ();
+ }
+ catch (lookup::unable_to_resolve const& e)
+ {
+ error (im.loc) << "unable to resolve name '" << e.name ()
+ << "' in db pragma member" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ // Handle container indexes.
+ //
+ if (j != in.members.end ())
+ {
+ // Do some sanity checks.
+ //
+ if (in.members.size () != 1)
+ {
+ error (in.loc) << "multiple data members specified for a "
+ << "container index" << endl;
+ throw operation_failed ();
+ }
+
+ if (tt != CPP_DOT || lex_.next (tl, &tn) != CPP_NAME ||
+ (tl != "id" && tl != "index"))
+ {
+ error (j->loc) << ".id or .index special member expected in a "
+ << "container index" << endl;
+ throw operation_failed ();
+ }
+
+ string n (tl);
+
+ if (lex_.next (tl, &tn) != CPP_EOF)
+ {
+ error (j->loc) << "unexpected text after ." << n << " in "
+ << "db pragma member" << endl;
+ throw operation_failed ();
+ }
+
+ // Move this index to the container member.
+ //
+ j->path.back ()->set (n + "-index", *i);
+ i = ins.erase (i);
+ continue;
+ }
+
+ // Now assign the name if the index is unnamed.
+ //
+ if (in.name.empty ())
+ {
+ // Make sure there is only one member.
+ //
+ if (in.members.size () > 1)
+ {
+ error (in.loc) << "unnamed index with more than one data "
+ << "member" << endl;
+ throw operation_failed ();
+ }
+
+ // Generally, we want the index name to be based on the column
+ // name. This is straightforward for single-column members. In
+ // case of a composite member, we will need to use the column
+ // prefix which is based on the data member name, unless
+ // overridden by the user. In the latter case the prefix can
+ // be empty, in which case we will just fall back on the
+ // member's public name.
+ //
+ in.name = column_name (in.members.front ().path);
+
+ if (in.name.empty ())
+ in.name = public_name_db (*in.members.front ().path.back ());
+
+ // In case of a composite member, column_name() return a column
+ // prefix which already includes the trailing underscore.
+ //
+ if (in.name[in.name.size () - 1] != '_')
+ in.name += '_';
+
+ in.name += 'i';
+ }
+
+ ++i;
+ }
+ }
+
//
// View.
//
@@ -2251,14 +2479,14 @@ namespace relational
name += "::";
name += r.name;
- lexer.start (name);
+ lex_.start (name);
string t;
- for (cpp_ttype tt (lexer.next (t));
+ for (cpp_ttype tt (lex_.next (t));
tt != CPP_EOF;
- tt = lexer.next (t))
+ tt = lex_.next (t))
{
- cxx_token ct (lexer.location (), tt);
+ cxx_token ct (lex_.location (), tt);
ct.literal = t;
i->cond.push_back (ct);
}
@@ -2618,16 +2846,16 @@ namespace relational
//
try
{
- lexer.start (ptr);
+ lex_.start (ptr);
ptr.clear ();
string t;
bool punc (false);
bool scoped (false);
- for (cpp_ttype tt (lexer.next (t));
+ for (cpp_ttype tt (lex_.next (t));
tt != CPP_EOF;
- tt = lexer.next (t))
+ tt = lex_.next (t))
{
if (punc && tt > CPP_LAST_PUNCTUATOR)
ptr += ' ';
@@ -2750,12 +2978,12 @@ namespace relational
tree tn;
cpp_ttype tt, ptt;
- nested_lexer.start (qn);
- tt = nested_lexer.next (tl, &tn);
+ nlex_.start (qn);
+ tt = nlex_.next (tl, &tn);
string name;
return lookup::resolve_scoped_name (
- nested_lexer, tt, tl, tn, ptt, scope, name, is_type);
+ nlex_, tt, tl, tn, ptt, scope, name, is_type);
}
catch (cxx_lexer::invalid_input const&)
{
@@ -2768,8 +2996,8 @@ namespace relational
}
private:
- cxx_string_lexer lexer;
- cxx_string_lexer nested_lexer;
+ cxx_string_lexer lex_;
+ cxx_string_lexer nlex_; // Nested lexer.
data_member member_;
traversal::names member_names_;
diff --git a/odb/relational/schema.hxx b/odb/relational/schema.hxx
index 6551119..e6d4824 100644
--- a/odb/relational/schema.hxx
+++ b/odb/relational/schema.hxx
@@ -494,13 +494,10 @@ namespace relational
}
virtual void
- create (sema_rel::index& in)
+ columns (sema_rel::index& in)
{
using sema_rel::index;
- os << "CREATE INDEX " << name (in) << endl
- << " ON " << table_name (in) << " (";
-
for (index::contains_iterator i (in.contains_begin ());
i != in.contains_end ();
++i)
@@ -515,9 +512,31 @@ namespace relational
}
os << quote_id (i->column ().name ());
+
+ if (!i->options ().empty ())
+ os << ' ' << i->options ();
}
+ }
+
+ virtual void
+ create (sema_rel::index& in)
+ {
+ // Default implementation that ignores the method.
+ //
+ os << "CREATE ";
+
+ if (!in.type ().empty ())
+ os << in.type () << ' ';
+
+ os << "INDEX " << name (in) << endl
+ << " ON " << table_name (in) << " (";
+
+ columns (in);
os << ")" << endl;
+
+ if (!in.options ().empty ())
+ os << ' ' << in.options () << endl;
}
protected:
diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx
index ee7ac21..a338b09 100644
--- a/odb/relational/source.cxx
+++ b/odb/relational/source.cxx
@@ -3436,7 +3436,7 @@ namespace relational
//
tree type;
decl = lookup::resolve_scoped_name (
- l, tt, tl, tn, ptt, scope, name, false, &type);
+ l, tt, tl, tn, ptt, scope, name, false, false, &type);
type = TYPE_MAIN_VARIANT (type);
diff --git a/odb/relational/validator.cxx b/odb/relational/validator.cxx
index ec78e8f..1269406 100644
--- a/odb/relational/validator.cxx
+++ b/odb/relational/validator.cxx
@@ -2,6 +2,7 @@
// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC
// license : GNU GPL v3; see accompanying LICENSE file
+#include <map>
#include <iostream>
#include <odb/diagnostics.hxx>
@@ -14,6 +15,95 @@ using namespace std;
namespace relational
{
+ namespace
+ {
+ //
+ // Pass 2.
+ //
+
+ struct class2: traversal::class_, context
+ {
+ class2 (bool& valid)
+ : valid_ (valid)
+ {
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ if (object (c))
+ traverse_object (c);
+ else if (view (c))
+ traverse_view (c);
+ else if (composite (c))
+ traverse_composite (c);
+
+ // Make sure indexes are not defined for anything other than objects.
+ //
+ if (c.count ("index") && !object (c))
+ {
+ indexes& ins (c.get<indexes> ("index"));
+
+ for (indexes::iterator i (ins.begin ()); i != ins.end (); ++i)
+ {
+ error (i->loc) << "index definition on a non-persistent class"
+ << endl;
+ valid_ = false;
+ }
+ }
+ }
+
+ virtual void
+ traverse_object (type& c)
+ {
+ // Validate indexes.
+ //
+ {
+ indexes& ins (c.get<indexes> ("index"));
+
+ // Make sure index members are not transient or containers.
+ //
+ for (indexes::iterator i (ins.begin ()); i != ins.end (); ++i)
+ {
+ index& in (*i);
+
+ for (index::members_type::iterator i (in.members.begin ());
+ i != in.members.end (); ++i)
+ {
+ index::member& im (*i);
+ semantics::data_member& m (*im.path.back ());
+
+ if (transient (m))
+ {
+ error (im.loc) << "index member is transient" << endl;
+ valid_ = false;
+ }
+
+ if (container (m))
+ {
+ error (im.loc) << "index member is a container" << endl;
+ valid_ = false;
+ }
+ }
+ }
+ }
+ }
+
+ virtual void
+ traverse_view (type&)
+ {
+ }
+
+ virtual void
+ traverse_composite (type&)
+ {
+ }
+
+ public:
+ bool& valid_;
+ };
+ }
+
void validator::
validate (options const&,
features&,
@@ -95,5 +185,33 @@ namespace relational
if (!valid)
throw failed ();
+
+ if (pass == 1)
+ {
+ }
+ else
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (true);
+ traversal::namespace_ ns;
+ class2 c (valid);
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (true);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (u);
+ }
+
+ if (!valid)
+ throw failed ();
}
}
diff --git a/odb/semantics/elements.cxx b/odb/semantics/elements.cxx
index 4b8fcea..ddae187 100644
--- a/odb/semantics/elements.cxx
+++ b/odb/semantics/elements.cxx
@@ -27,13 +27,13 @@ namespace semantics
//
node::
node (path const& file, size_t line, size_t column, tree tn)
- : tree_node_ (tn), file_ (file), line_ (line), column_ (column)
+ : tree_node_ (tn), loc_ (file, line, column)
{
}
node::
node ()
- : file_ ("")
+ : loc_ (0)
{
// GCC plugin machinery #define's abort as a macro.
//
diff --git a/odb/semantics/elements.hxx b/odb/semantics/elements.hxx
index 59e058d..ac62ff9 100644
--- a/odb/semantics/elements.hxx
+++ b/odb/semantics/elements.hxx
@@ -20,6 +20,7 @@
#include <cutl/compiler/context.hxx>
#include <odb/gcc-fwd.hxx>
+#include <odb/location.hxx>
namespace semantics
{
@@ -134,22 +135,30 @@ namespace semantics
}
public:
+ typedef ::location location_type;
+
path const&
file () const
{
- return file_;
+ return loc_.file;
}
size_t
line () const
{
- return line_;
+ return loc_.line;
}
size_t
column () const
{
- return column_;
+ return loc_.column;
+ }
+
+ location_type const&
+ location () const
+ {
+ return loc_;
}
public:
@@ -204,9 +213,7 @@ namespace semantics
tree tree_node_;
unit_type* unit_;
- path file_;
- size_t line_;
- size_t column_;
+ location_type loc_;
};
//
diff --git a/odb/semantics/relational/index.hxx b/odb/semantics/relational/index.hxx
index af90b12..bf08a23 100644
--- a/odb/semantics/relational/index.hxx
+++ b/odb/semantics/relational/index.hxx
@@ -17,13 +17,42 @@ namespace semantics
class index: public key
{
public:
- index (string const& id): key (id) {}
+ index (string const& id,
+ string const& t = string (),
+ string const& m = string (),
+ string const& o = string ())
+ : key (id), type_ (t), method_ (m), options_ (o)
+ {
+ }
+
+ string const&
+ type () const
+ {
+ return type_;
+ }
+
+ string const&
+ method () const
+ {
+ return method_;
+ }
+
+ string const&
+ options () const
+ {
+ return options_;
+ }
virtual string
kind () const
{
return "index";
}
+
+ private:
+ string type_; // E.g., "UNIQUE", etc.
+ string method_; // E.g., "BTREE", etc.
+ string options_; // Database-specific index options.
};
}
}
diff --git a/odb/semantics/relational/key.hxx b/odb/semantics/relational/key.hxx
index 40e7499..3b86f1c 100644
--- a/odb/semantics/relational/key.hxx
+++ b/odb/semantics/relational/key.hxx
@@ -32,7 +32,15 @@ namespace semantics
return *column_;
}
+ string const&
+ options () const
+ {
+ return options_;
+ }
+
public:
+ contains (string const& o = string ()) : options_ (o) {}
+
void
set_left_node (key_type& n)
{
@@ -48,6 +56,7 @@ namespace semantics
protected:
key_type* key_;
column_type* column_;
+ string options_;
};
class key: public unameable
diff --git a/odb/validator.cxx b/odb/validator.cxx
index 597da01..0f67100 100644
--- a/odb/validator.cxx
+++ b/odb/validator.cxx
@@ -1236,57 +1236,54 @@ validate (options const& ops,
unsigned short pass)
{
bool valid (true);
+ auto_ptr<context> ctx (create_context (cerr, u, ops, f, 0));
+ if (pass == 1)
{
- auto_ptr<context> ctx (create_context (cerr, u, ops, f, 0));
-
- if (pass == 1)
- {
- traversal::unit unit;
- traversal::defines unit_defines;
- traversal::declares unit_declares;
- typedefs1 unit_typedefs (unit_declares);
- traversal::namespace_ ns;
- value_type vt (valid);
- class1 c (valid, vt);
-
- unit >> unit_defines >> ns;
- unit_defines >> c;
- unit >> unit_declares >> vt;
- unit >> unit_typedefs >> c;
-
- traversal::defines ns_defines;
- traversal::declares ns_declares;
- typedefs1 ns_typedefs (ns_declares);
-
- ns >> ns_defines >> ns;
- ns_defines >> c;
- ns >> ns_declares >> vt;
- ns >> ns_typedefs >> c;
-
- unit.dispatch (u);
- }
- else
- {
- traversal::unit unit;
- traversal::defines unit_defines;
- typedefs unit_typedefs (true);
- traversal::namespace_ ns;
- class2 c (valid);
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ traversal::declares unit_declares;
+ typedefs1 unit_typedefs (unit_declares);
+ traversal::namespace_ ns;
+ value_type vt (valid);
+ class1 c (valid, vt);
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_declares >> vt;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ traversal::declares ns_declares;
+ typedefs1 ns_typedefs (ns_declares);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_declares >> vt;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (u);
+ }
+ else
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (true);
+ traversal::namespace_ ns;
+ class2 c (valid);
- unit >> unit_defines >> ns;
- unit_defines >> c;
- unit >> unit_typedefs >> c;
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
- traversal::defines ns_defines;
- typedefs ns_typedefs (true);
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (true);
- ns >> ns_defines >> ns;
- ns_defines >> c;
- ns >> ns_typedefs >> c;
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
- unit.dispatch (u);
- }
+ unit.dispatch (u);
}
if (!valid)