summaryrefslogtreecommitdiff
path: root/odb/relational
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/relational
parentadfa9bbd04cd3571932ee7675344ca723bfa1eab (diff)
Add support for defining indexes
New db pragma qualifier: index. New tests: common/index, mysql/index, pgsql/index.
Diffstat (limited to 'odb/relational')
-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
9 files changed, 696 insertions, 82 deletions
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 ();
}
}