aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-07-03 18:23:51 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-07-03 18:23:51 +0200
commit80b868be1e7c249daa714b0c7a5f87694edb8664 (patch)
treed8e0dd6eca09d33d70224f7289ae096a0976cd6b
parent89ba09f073b103953c53e63bd78f644973d9154e (diff)
Implement nested id support
Now the 'id' specifier can optionally include the data member path to the id inside the composite value. For example: #pragma db id(first) std::pair<int, int> p; Note that one somewhat counter-intuitive aspect of this new feature is that the whole member marked with id ('p' in the above example) and not just the actual id member ('p.first' in the above example) is treated as readonly. Such nested id also cannot be automatically assigned (auto specifier).
-rw-r--r--odb/common-query.cxx2
-rw-r--r--odb/common.cxx38
-rw-r--r--odb/common.hxx18
-rw-r--r--odb/context.cxx40
-rw-r--r--odb/context.hxx79
-rw-r--r--odb/header.cxx8
-rw-r--r--odb/inline.cxx31
-rw-r--r--odb/pragma.cxx38
-rw-r--r--odb/processor.cxx16
-rw-r--r--odb/relational/common.txx6
-rw-r--r--odb/relational/header.cxx10
-rw-r--r--odb/relational/inline.hxx8
-rw-r--r--odb/relational/model.hxx8
-rw-r--r--odb/relational/mssql/source.cxx14
-rw-r--r--odb/relational/oracle/source.cxx6
-rw-r--r--odb/relational/pgsql/header.cxx2
-rw-r--r--odb/relational/pgsql/source.cxx10
-rw-r--r--odb/relational/processor.cxx12
-rw-r--r--odb/relational/source.cxx76
-rw-r--r--odb/relational/source.hxx24
-rw-r--r--odb/relational/validator.cxx16
-rw-r--r--odb/semantics/elements.hxx22
-rw-r--r--odb/validator.cxx79
23 files changed, 407 insertions, 156 deletions
diff --git a/odb/common-query.cxx b/odb/common-query.cxx
index 25007a0..ade621a 100644
--- a/odb/common-query.cxx
+++ b/odb/common-query.cxx
@@ -678,7 +678,7 @@ traverse_pointer (semantics::data_member& m, semantics::class_& c)
string name (public_name (m));
- semantics::data_member& id (*id_member (c));
+ data_member_path& id (*id_member (c));
semantics::names* hint;
semantics::type& t (utype (id, hint));
diff --git a/odb/common.cxx b/odb/common.cxx
index 8dc995d..6d341b8 100644
--- a/odb/common.cxx
+++ b/odb/common.cxx
@@ -288,6 +288,42 @@ traverse_post (semantics::nameable&)
}
void object_columns_base::
+traverse (semantics::data_member& m)
+{
+ traverse_pre (m);
+
+ semantics::type& t (utype (m));
+ semantics::class_* c (object_pointer (t));
+ semantics::type* rt (c == 0 ? &t : &utype (*id_member (*c)));
+
+ root_ = &m;
+
+ // It would seem natural to add m to member_path_ so that we don't
+ // have these two cases. However, for member path to work correctly
+ // with readonly() we also have to have corresponding member_scope,
+ // which is a whole different level of complexity.
+ //
+ root_id_ = member_path_.empty ()
+ ? context::id (m)
+ : context::id (member_path_) != 0;
+ root_op_ = (c != 0);
+ root_null_ = context::null (m);
+
+
+ if (root_op_)
+ traverse_pointer (m, *c);
+ else
+ traverse_member (m, *rt);
+
+ if (!first_ && composite_wrapper (*rt))
+ flush ();
+
+ root_ = 0;
+
+ traverse_post (m);
+}
+
+void object_columns_base::
traverse (semantics::data_member& m,
semantics::type& t,
std::string const& kp,
@@ -305,7 +341,7 @@ traverse (semantics::data_member& m,
semantics::type* rt (c == 0 ? &t : &utype (*id_member (*c)));
root_ = &m;
- root_id_ = (kp.empty () ? context::id (m) : kp == "id");
+ root_id_ = (kp == "id");
root_op_ = (c != 0);
root_null_ = context::null (m, kp);
diff --git a/odb/common.hxx b/odb/common.hxx
index 104351a..ef85a4b 100644
--- a/odb/common.hxx
+++ b/odb/common.hxx
@@ -278,10 +278,7 @@ public:
// value type, or an object pointer (with a simple or composite id).
//
virtual void
- traverse (semantics::data_member& m)
- {
- traverse (m, utype (m), string (), string ());
- }
+ traverse (semantics::data_member&);
virtual void
traverse (semantics::data_member& m, column_prefix const& cp)
@@ -293,6 +290,17 @@ public:
}
virtual void
+ traverse (data_member_path& mp)
+ {
+ data_member_path op (member_path_);
+ member_path_ = mp;
+ traverse (*mp.back (), column_prefix (mp));
+ member_path_ = op;
+ }
+
+ // Should only be used for containers.
+ //
+ virtual void
traverse (semantics::data_member&,
semantics::type&,
string const& key_prefix,
@@ -333,7 +341,7 @@ protected:
}
else
return context::column_type (
- member_path_, key_prefix_, (root_ != 0 && (root_id_ || root_op_)));
+ member_path_, key_prefix_, root_ != 0 && (root_id_ || root_op_));
}
bool
diff --git a/odb/context.cxx b/odb/context.cxx
index 4e354d8..59058ba 100644
--- a/odb/context.cxx
+++ b/odb/context.cxx
@@ -1001,14 +1001,25 @@ context* context::current_;
semantics::data_member* context::
id (data_member_path const& mp)
{
- for (data_member_path::const_reverse_iterator i (mp.rbegin ());
- i != mp.rend (); ++i)
- {
- if (id (**i))
- return *i;
- }
+ semantics::data_member* idf (mp.front ());
- return 0;
+ if (!id (*idf))
+ return 0;
+
+ // This is for special ids, such as polymorphic-ref, which
+ // don't have "id-member" set (and we want to keep it that
+ // way since it is not really a full-fledged id).
+ //
+ if (idf->get<string> ("id").empty ()) // Not a nested id.
+ return idf;
+
+ const data_member_path& id (
+ *id_member (
+ dynamic_cast<semantics::class_&> (idf->scope ())));
+
+ // Now we need to make sure id is a prefix of mp;
+ //
+ return mp.sub (id) ? idf : 0;
}
semantics::data_member* context::
@@ -1696,8 +1707,9 @@ inc_member (semantics::data_member& m,
}
}
-data_member_path context::
-resolve_data_members (semantics::class_& c,
+void context::
+resolve_data_members (data_member_path& r,
+ semantics::class_& c,
const string& name,
const location& l,
cxx_string_lexer& lex)
@@ -1705,8 +1717,6 @@ resolve_data_members (semantics::class_& c,
using semantics::class_;
using semantics::data_member;
- data_member_path r;
-
// The name was already verified to be syntactically correct so
// we don't need to do any extra error checking in this area.
//
@@ -1722,7 +1732,7 @@ resolve_data_members (semantics::class_& c,
r.push_back (&m);
if (container (m))
- return r;
+ return;
// Resolve nested members if any.
//
@@ -1749,7 +1759,7 @@ resolve_data_members (semantics::class_& c,
r.push_back (&nm);
if (container (nm))
- return r;
+ return;
}
}
catch (semantics::unresolved const& e)
@@ -1775,8 +1785,6 @@ resolve_data_members (semantics::class_& c,
throw operation_failed ();
}
-
- return r;
}
bool context::
@@ -2177,7 +2185,7 @@ column_name (semantics::data_member& m, column_prefix const& cp) const
string n (column_name (m, d));
n = compose_name (cp.prefix, n);
- // If any component is derived, the run it through the SQL name regex.
+ // If any component is derived, then run it through the SQL name regex.
//
if (d || cp.derived)
n = transform_name (n, sql_name_column);
diff --git a/odb/context.hxx b/odb/context.hxx
index 5eef193..6398a5f 100644
--- a/odb/context.hxx
+++ b/odb/context.hxx
@@ -95,6 +95,24 @@ struct data_member_path: std::vector<semantics::data_member*>
explicit
data_member_path (semantics::data_member& m) {push_back (&m);}
+
+ // Return true if this is a sub-path of (or equal to) the
+ // specified path.
+ //
+ bool
+ sub (const data_member_path& p) const
+ {
+ size_t n (p.size ());
+
+ if (n > size ())
+ return false;
+
+ for (size_t i (0); i != n; ++i)
+ if ((*this)[i] != p[i])
+ return false;
+
+ return true;
+ }
};
// Class inheritance chain, from the most derived to base.
@@ -276,6 +294,11 @@ struct table_column
std::string column;
bool expr; // True if column is an expression, and therefore should not
// be quoted.
+
+ table_column () {}
+
+ explicit
+ table_column (const std::string& c): column (c), expr (false) {}
};
//
@@ -655,6 +678,29 @@ public:
string const& key_prefix = string (),
const custom_cxx_type** translation = 0);
+ static semantics::type&
+ utype (const data_member_path& mp, const custom_cxx_type** translation = 0)
+ {
+ return utype (*mp.back (), translation);
+ }
+
+ static semantics::type&
+ utype (const data_member_path& mp,
+ string const& key_prefix,
+ const custom_cxx_type** translation = 0)
+ {
+ return utype (*mp.back (), key_prefix, translation);
+ }
+
+ static semantics::type&
+ utype (const data_member_path& mp,
+ semantics::names*& hint,
+ string const& key_prefix = string (),
+ const custom_cxx_type** translation = 0)
+ {
+ return utype (*mp.back (), hint, key_prefix, translation);
+ }
+
// For arrays this function returns true if the (innermost) element
// type is const.
//
@@ -725,12 +771,24 @@ public:
// This function stops if it encounters a container leaving lex usable
// to continue parsing.
//
- data_member_path
- resolve_data_members (semantics::class_& scope,
+ void
+ resolve_data_members (data_member_path& append,
+ semantics::class_& scope,
const std::string& name,
const location&,
cxx_string_lexer&);
+ data_member_path
+ resolve_data_members (semantics::class_& scope,
+ const std::string& name,
+ const location& l,
+ cxx_string_lexer& lex)
+ {
+ data_member_path r;
+ resolve_data_members (r, scope, name, l, lex);
+ return r;
+ }
+
// Predicates.
//
public:
@@ -1015,7 +1073,16 @@ public:
static bool
auto_ (semantics::data_member& m)
{
- return m.count ("auto");
+ return id (m) && m.count ("auto");
+ }
+
+ // Must be a path returned by id(). In other words, it assumes
+ // the path is to the id member.
+ //
+ static bool
+ auto_ (data_member_path& mp)
+ {
+ return mp.front ()->count ("auto");
}
// The member scope is used to override readonly status when a readonly
@@ -1438,13 +1505,13 @@ public:
static column_count_type
column_count (semantics::class_&, object_section* = 0);
- static semantics::data_member*
+ static data_member_path*
id_member (semantics::class_& c)
{
- // Set by the validator. May not be there for reuse-abstract
+ // Set by the processor. May not be there for reuse-abstract
// classes or classes without object id.
//
- return c.get<semantics::data_member*> ("id-member", 0);
+ return c.count ("id-member") ? &c.get<data_member_path> ("id-member") : 0;
}
// Object pointer information.
diff --git a/odb/header.cxx b/odb/header.cxx
index 0a01311..2155ed8 100644
--- a/odb/header.cxx
+++ b/odb/header.cxx
@@ -60,9 +60,10 @@ traverse_object (type& c)
{
using semantics::data_member;
- data_member* id (id_member (c));
+ data_member_path* id (id_member (c));
+ data_member* idf (id ? id->front () : 0);
bool auto_id (id && auto_ (*id));
- bool base_id (id && &id->scope () != &c); // Comes from base.
+ bool base_id (id && &idf->scope () != &c); // Comes from base.
data_member* opt (context::optimistic (c));
@@ -157,8 +158,7 @@ traverse_object (type& c)
{
if (base_id)
{
- semantics::class_& b (
- dynamic_cast<semantics::class_&> (id->scope ()));
+ semantics::class_& b (dynamic_cast<semantics::class_&> (idf->scope ()));
string const& type (class_fq_name (b));
os << "typedef object_traits< " << type << " >::id_type id_type;";
diff --git a/odb/inline.cxx b/odb/inline.cxx
index 1af84b6..87f1f5f 100644
--- a/odb/inline.cxx
+++ b/odb/inline.cxx
@@ -112,15 +112,16 @@ traverse_object (type& c)
{
using semantics::data_member;
- data_member* id (id_member (c));
+ data_member_path* id (id_member (c));
+ data_member* idf (id ? id->front () : 0);
bool auto_id (id && auto_ (*id));
- bool base_id (id && &id->scope () != &c); // Comes from base.
+ bool base_id (id && &idf->scope () != &c); // Comes from base.
data_member* opt (context::optimistic (c));
// Base class that contains the object id.
//
- type* base (id != 0 && base_id ? dynamic_cast<type*> (&id->scope ()) : 0);
+ type* base (base_id ? dynamic_cast<type*> (&idf->scope ()) : 0);
bool poly (polymorphic (c));
bool abst (abstract (c));
@@ -152,16 +153,26 @@ traverse_object (type& c)
" >::id (o);";
else
{
- // Get the id using the accessor expression. If this is not
- // a synthesized expression, then output its location for
- // easier error tracking.
+ // Get the id using the accessor expressions.
//
- member_access& ma (id->get<member_access> ("get"));
+ string r ("o");
- if (!ma.synthesized)
- os << "// From " << location_string (ma.loc, true) << endl;
+ for (data_member_path::const_iterator b (id->begin ()), i (b);
+ i != id->end ();
+ ++i)
+ {
+ member_access& ma ((*i)->get<member_access> ("get"));
- os << "return " << ma.translate ("o") << ";";
+ // If this is not a synthesized expression, then output its
+ // location for easier error tracking.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ r = ma.translate (r);
+ }
+
+ os << "return " << r << ";";
}
}
diff --git a/odb/pragma.cxx b/odb/pragma.cxx
index 549a0f6..046140e 100644
--- a/odb/pragma.cxx
+++ b/odb/pragma.cxx
@@ -1893,12 +1893,50 @@ handle_pragma (cxx_lexer& l,
}
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")
{
diff --git a/odb/processor.cxx b/odb/processor.cxx
index f7a2be2..5dc49f9 100644
--- a/odb/processor.cxx
+++ b/odb/processor.cxx
@@ -46,7 +46,7 @@ namespace
id_tree_type (semantics::names*& hint)
{
context& c (context::current ());
- semantics::data_member& id (*context::id_member (*c.top_object));
+ data_member_path& id (*context::id_member (*c.top_object));
return &c.utype (id, hint);
}
@@ -2109,7 +2109,10 @@ namespace
virtual void
traverse_object_pre (type& c)
{
- semantics::class_* poly_root (polymorphic (c));
+ using semantics::class_;
+ using semantics::data_member;
+
+ class_* poly_root (polymorphic (c));
// Sections.
//
@@ -2199,7 +2202,8 @@ namespace
using namespace semantics;
using semantics::data_member;
- data_member* idm (id_member (*poly_root));
+ data_member_path& id (*id_member (*poly_root));
+ data_member* idm (id.front ());
if (poly_root != &c)
{
@@ -2248,8 +2252,12 @@ namespace
// Mark it as a special kind of id.
//
- m.set ("id", true);
+ m.set ("id", string ());
m.set ("polymorphic-ref", true);
+
+ // Make sure we also use the same column name as the root.
+ //
+ m.set ("column", table_column (column_name (id)));
}
else
{
diff --git a/odb/relational/common.txx b/odb/relational/common.txx
index 5e5c5ac..116cfc2 100644
--- a/odb/relational/common.txx
+++ b/odb/relational/common.txx
@@ -36,9 +36,9 @@ namespace relational
{
// A pointer in view might point to an object without id.
//
- semantics::data_member* idm (id_member (*c));
- semantics::type& t (utype (idm != 0 ? *idm : m, &ct));
- semantics::class_* comp (idm != 0 ? composite_wrapper (t) : 0);
+ data_member_path* id (id_member (*c));
+ semantics::type& t (id != 0 ? utype (*id, &ct) : utype (m, &ct));
+ semantics::class_* comp (id != 0 ? composite_wrapper (t) : 0);
member_info mi (m,
(comp != 0 ? *comp : t),
diff --git a/odb/relational/header.cxx b/odb/relational/header.cxx
index 4b8b193..b64ab2a 100644
--- a/odb/relational/header.cxx
+++ b/odb/relational/header.cxx
@@ -12,9 +12,11 @@ traverse_object (type& c)
{
using semantics::data_member;
- data_member* id (id_member (c));
+ data_member_path* id (id_member (c));
+ data_member* idf (id ? id->front () : 0);
+ data_member* idb (id ? id->back () : 0);
bool auto_id (id && auto_ (*id));
- bool base_id (id && &id->scope () != &c); // Comes from base.
+ bool base_id (id && &idf->scope () != &c); // Comes from base.
data_member* opt (context::optimistic (c));
@@ -120,7 +122,7 @@ traverse_object (type& c)
else
{
semantics::class_& b (
- dynamic_cast<semantics::class_&> (id->scope ()));
+ dynamic_cast<semantics::class_&> (idf->scope ()));
os << "typedef object_traits_impl< " << class_fq_name (b) << ", " <<
"id_" << db << " >::id_image_type id_image_type;"
@@ -132,7 +134,7 @@ traverse_object (type& c)
os << "struct id_image_type"
<< "{";
- id_image_member_->traverse (*id);
+ id_image_member_->traverse (*idb);
if (opt != 0)
version_image_member_->traverse (*opt);
diff --git a/odb/relational/inline.hxx b/odb/relational/inline.hxx
index 090779f..583cb13 100644
--- a/odb/relational/inline.hxx
+++ b/odb/relational/inline.hxx
@@ -214,16 +214,16 @@ namespace relational
{
using semantics::data_member;
- data_member* id (id_member (c));
+ data_member_path* id (id_member (c));
+ data_member* idf (id ? id->front () : 0);
bool auto_id (id && auto_ (*id));
- bool base_id (id && &id->scope () != &c); // Comes from base.
+ bool base_id (id && &idf->scope () != &c); // Comes from base.
data_member* optimistic (context::optimistic (c));
// Base class that contains the object id and version for optimistic
// concurrency.
//
- type* base (
- id != 0 && base_id ? dynamic_cast<type*> (&id->scope ()) : 0);
+ type* base (base_id ? dynamic_cast<type*> (&idf->scope ()) : 0);
type* poly_root (context::polymorphic (c));
bool poly (poly_root != 0);
diff --git a/odb/relational/model.hxx b/odb/relational/model.hxx
index 00b3b46..13c67d7 100644
--- a/odb/relational/model.hxx
+++ b/odb/relational/model.hxx
@@ -342,10 +342,10 @@ namespace relational
// Get referenced columns.
//
{
- semantics::data_member& idm (*id_member (c));
+ data_member_path& id (*id_member (c));
instance<object_columns_list> ocl;
- ocl->traverse (idm);
+ ocl->traverse (id);
for (object_columns_list::iterator i (ocl->begin ());
i != ocl->end (); ++i)
@@ -634,10 +634,10 @@ namespace relational
// Get referenced columns.
//
{
- data_member& idm (*id_member (*context::top_object));
+ data_member_path& id (*id_member (*context::top_object));
instance<object_columns_list> ocl;
- ocl->traverse (idm);
+ ocl->traverse (id);
for (object_columns_list::iterator i (ocl->begin ());
i != ocl->end (); ++i)
diff --git a/odb/relational/mssql/source.cxx b/odb/relational/mssql/source.cxx
index 89b5702..42c29a7 100644
--- a/odb/relational/mssql/source.cxx
+++ b/odb/relational/mssql/source.cxx
@@ -44,10 +44,9 @@ namespace relational
string const& column)
{
// Don't add a column for auto id in the INSERT statement.
+ // Only simple, direct id can be auto.
//
- if (sk_ == statement_insert &&
- key_prefix_.empty () &&
- context::id (m) && auto_(m)) // Only simple id can be auto.
+ if (sk_ == statement_insert && key_prefix_.empty () && auto_ (m))
return false;
// Don't update the ROWVERSION column explicitly.
@@ -987,7 +986,7 @@ namespace relational
// See if we have auto id or ROWVERSION version.
//
- semantics::data_member* id (id_member (c));
+ data_member_path* id (id_member (c));
semantics::data_member* ver (optimistic (c));
if (id != 0 && !auto_ (*id))
@@ -1047,7 +1046,8 @@ namespace relational
throw operation_failed ();
}
- r = "; SELECT " + convert_from ("SCOPE_IDENTITY()", *id);
+ r = "; SELECT " +
+ convert_from ("SCOPE_IDENTITY()", *id->back ());
}
return r;
@@ -1061,8 +1061,8 @@ namespace relational
// Top-level auto id column.
//
if (id != 0)
- r += "INSERTED." + convert_from (
- column_qname (*id, column_prefix ()), *id);
+ r += "INSERTED." +
+ convert_from (column_qname (*id), *id->back ());
// Top-level version column.
//
diff --git a/odb/relational/oracle/source.cxx b/odb/relational/oracle/source.cxx
index 310eac6..802bc99 100644
--- a/odb/relational/oracle/source.cxx
+++ b/odb/relational/oracle/source.cxx
@@ -604,16 +604,16 @@ namespace relational
if (p == persist_after_values)
{
- semantics::data_member* id (id_member (c));
+ data_member_path* id (id_member (c));
type* poly_root (polymorphic (c));
bool poly_derived (poly_root != 0 && poly_root != &c);
// Top-level auto id.
//
- if (id != 0 && !poly_derived && id->count ("auto"))
+ if (id != 0 && !poly_derived && auto_ (*id))
r = "RETURNING " +
- convert_from (column_qname (*id, column_prefix ()), *id) +
+ convert_from (column_qname (*id), *id->back ()) +
" INTO " + qp.next ();
}
diff --git a/odb/relational/pgsql/header.cxx b/odb/relational/pgsql/header.cxx
index 19fa573..b3566d8 100644
--- a/odb/relational/pgsql/header.cxx
+++ b/odb/relational/pgsql/header.cxx
@@ -31,7 +31,7 @@ namespace relational
if (abst && !poly)
return;
- semantics::data_member* id (id_member (c));
+ data_member_path* id (id_member (c));
semantics::data_member* optimistic (context::optimistic (c));
column_count_type const& cc (column_count (c));
diff --git a/odb/relational/pgsql/source.cxx b/odb/relational/pgsql/source.cxx
index 2bbe232..b8270ac 100644
--- a/odb/relational/pgsql/source.cxx
+++ b/odb/relational/pgsql/source.cxx
@@ -583,16 +583,16 @@ namespace relational
if (p == persist_after_values)
{
- semantics::data_member* id (id_member (c));
+ data_member_path* id (id_member (c));
type* poly_root (polymorphic (c));
bool poly_derived (poly_root != 0 && poly_root != &c);
// Top-level auto id.
//
- if (id != 0 && !poly_derived && id->count ("auto"))
+ if (id != 0 && !poly_derived && auto_ (*id))
r = "RETURNING " +
- convert_from (column_qname (*id, column_prefix ()), *id);
+ convert_from (column_qname (*id), *id->back ());
}
return r;
@@ -610,7 +610,7 @@ namespace relational
if (abst && !poly)
return;
- semantics::data_member* id (id_member (c));
+ data_member_path* id (id_member (c));
semantics::data_member* optimistic (context::optimistic (c));
column_count_type const& cc (column_count (c));
@@ -706,7 +706,7 @@ namespace relational
statement_oids st (statement_insert);
st.traverse (c);
- // Empty array are not portable. So add a dummy member if we
+ // Empty array is not portable. So add a dummy member if we
// are not sending anything with the insert statement.
//
if (cc.total == cc.inverse + cc.optimistic_managed +
diff --git a/odb/relational/processor.cxx b/odb/relational/processor.cxx
index 2979a34..48ddba4 100644
--- a/odb/relational/processor.cxx
+++ b/odb/relational/processor.cxx
@@ -27,8 +27,8 @@ namespace relational
id_column_type ()
{
context& c (context::current ());
- semantics::data_member& id (*context::id_member (*c.top_object));
- return id.get<string> ("column-type");
+ data_member_path& id (*context::id_member (*c.top_object));
+ return id.back ()->get<string> ("column-id-type");
}
struct data_member: traversal::data_member, context
@@ -85,7 +85,7 @@ namespace relational
// This is an object pointer. The column type is the pointed-to
// object id type.
//
- semantics::data_member& id (*id_member (*c));
+ semantics::data_member& id (*id_member (*c)->back ());
semantics::names* idhint;
semantics::type& idt (utype (id, idhint));
@@ -220,7 +220,7 @@ namespace relational
//
{
semantics::class_& r (*object_pointer (t));
- semantics::data_member& id (*id_member (r));
+ semantics::data_member& id (*id_member (r)->front ());
if (id.count ("column"))
m.set ("column", id.get<table_column> ("column"));
@@ -306,7 +306,7 @@ namespace relational
// This is an object pointer. The column type is the pointed-to
// object id type.
//
- semantics::data_member& id (*id_member (*c));
+ semantics::data_member& id (*id_member (*c)->back ());
semantics::names* idhint;
semantics::type& idt (utype (id, idhint));
@@ -824,7 +824,7 @@ namespace relational
m.set ("column-type", src_m->get<string> ("column-type"));
else if (semantics::class_* c = object_pointer (utype (*src_m)))
{
- semantics::data_member& id (*id_member (*c));
+ semantics::data_member& id (*id_member (*c)->back ());
if (id.count ("type"))
m.set ("column-type", id.get<string> ("column-type"));
diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx
index 83e3c40..3b96ab5 100644
--- a/odb/relational/source.cxx
+++ b/odb/relational/source.cxx
@@ -19,9 +19,11 @@ traverse_object (type& c)
{
using semantics::data_member;
- data_member* id (id_member (c));
+ data_member_path* id (id_member (c));
+ data_member* idf (id ? id->front () : 0);
+ data_member* idb (id ? id->back () : 0);
bool auto_id (id && auto_ (*id));
- bool base_id (id && &id->scope () != &c); // Comes from base.
+ bool base_id (id && &idf->scope () != &c); // Comes from base.
data_member* opt (optimistic (c));
@@ -42,7 +44,7 @@ traverse_object (type& c)
if (generate_grow)
{
grow = context::grow (c);
- grow_id = (id ? context::grow (*id) : false) ||
+ grow_id = (id ? context::grow (*idb) : false) ||
(opt ? context::grow (*opt) : false);
}
@@ -188,7 +190,7 @@ traverse_object (type& c)
<< "ODB_POTENTIALLY_UNUSED (db);"
<< endl
<< "id_type id;";
- init_id_value_member_id_image_->traverse (*id);
+ init_id_value_member_id_image_->traverse (*idb);
os << "return id;"
<< "}";
}
@@ -205,7 +207,35 @@ traverse_object (type& c)
<< "ODB_POTENTIALLY_UNUSED (db);"
<< endl
<< "id_type id;";
- init_id_value_member_->traverse (*id);
+
+ // Handle nested id.
+ //
+ if (id->size () > 1)
+ {
+ string var;
+
+ for (data_member_path::const_iterator i (id->begin ());
+ i != id->end ();
+ ++i)
+ {
+ // The same logic as in member_base.
+ //
+ if (!var.empty ())
+ var += "value."; // Composite.
+
+ string const& name ((*i)->name ());
+ var += name;
+
+ if (name[name.size () - 1] != '_')
+ var += '_';
+ }
+
+ instance<init_value_member> t ("id", var);
+ t->traverse (*idb);
+ }
+ else
+ init_id_value_member_->traverse (*idb);
+
os << "return id;"
<< "}";
}
@@ -339,7 +369,7 @@ traverse_object (type& c)
{
// The id reference comes first in the insert statement.
//
- os << "// " << id->name () << endl
+ os << "// " << idf->name () << endl
<< "//" << endl
<< "if (sk == statement_insert)"
<< "{"
@@ -358,7 +388,7 @@ traverse_object (type& c)
// The id reference comes last in the update statement.
//
if (!readonly)
- os << "// " << id->name () << endl
+ os << "// " << idf->name () << endl
<< "//" << endl
<< "if (sk == statement_update)"
<< "{"
@@ -398,7 +428,7 @@ traverse_object (type& c)
if (composite_wrapper (utype (*id)))
os << db << "::statement_kind sk (" << db << "::statement_select);";
- bind_id_member_->traverse (*id);
+ bind_id_member_->traverse (*idb);
if (opt != 0)
{
@@ -516,7 +546,7 @@ traverse_object (type& c)
if (composite_wrapper (utype (*id)))
os << db << "::statement_kind sk (" << db << "::statement_select);";
- init_id_image_member_->traverse (*id);
+ init_id_image_member_->traverse (*idb);
if (opt != 0)
{
@@ -1222,9 +1252,9 @@ traverse_object (type& c)
if (!poly_derived && auto_id && insert_send_auto_id)
{
- string const& n (id->name ());
+ string const& n (idf->name ());
string var ("im." + n + (n[n.size () - 1] == '_' ? "" : "_"));
- init_auto_id (*id, var);
+ init_auto_id (*idf, var); // idf == idb, since auto
os << endl;
}
@@ -1278,9 +1308,9 @@ traverse_object (type& c)
<< "throw object_already_persistent ();"
<< endl;
- if (!poly_derived && auto_id)
+ if (!poly_derived && auto_id) // idf == idb since auto
{
- set_member (*id, "obj", "id (sts.id_image ())", "db", "id_type");
+ set_member (*idf, "obj", "id (sts.id_image ())", "db", "id_type");
os << endl;
}
@@ -1486,9 +1516,9 @@ traverse_object (type& c)
// Extract auto id.
//
- if (auto_id)
+ if (auto_id) // idb == idf, since auto
{
- set_member (*id, "obj", "id (sts.id_image (i))", "db", "id_type");
+ set_member (*idf, "obj", "id (sts.id_image (i))", "db", "id_type");
os << endl;
}
@@ -4867,7 +4897,7 @@ traverse_view (type& c)
{
// container.value = pointer.id
//
- semantics::data_member& id (*id_member (*e.vo->obj));
+ data_member_path& id (*id_member (*e.vo->obj));
c_cols->traverse (imb, utype (id), "value", "value");
o_cols->traverse (id);
@@ -4877,7 +4907,7 @@ traverse_view (type& c)
{
// container.id = pointed-to.id
//
- semantics::data_member& id (*id_member (*vo->obj));
+ data_member_path& id (*id_member (*vo->obj));
c_cols->traverse (imb, utype (id), "id", "object_id", vo->obj);
o_cols->traverse (id);
@@ -4890,7 +4920,7 @@ traverse_view (type& c)
{
// container.id = pointer.id
//
- semantics::data_member& id (*id_member (*e.vo->obj));
+ data_member_path& id (*id_member (*e.vo->obj));
c_cols->traverse (
m, utype (id), "id", "object_id", e.vo->obj);
@@ -4901,7 +4931,7 @@ traverse_view (type& c)
{
// container.value = pointed-to.id
//
- semantics::data_member& id (*id_member (*vo->obj));
+ data_member_path& id (*id_member (*vo->obj));
c_cols->traverse (m, utype (id), "value", "value");
o_cols->traverse (id);
@@ -4961,7 +4991,7 @@ traverse_view (type& c)
{
// container.id = pointed-to.id
//
- semantics::data_member& id (*id_member (*vo->obj));
+ data_member_path& id (*id_member (*vo->obj));
c_cols->traverse (imb, utype (id), "id", "object_id", vo->obj);
o_cols->traverse (id);
@@ -4971,7 +5001,7 @@ traverse_view (type& c)
{
// container.value = pointer.id
//
- semantics::data_member& id (*id_member (*e.vo->obj));
+ data_member_path& id (*id_member (*e.vo->obj));
c_cols->traverse (imb, utype (id), "value", "value");
o_cols->traverse (id);
@@ -4984,7 +5014,7 @@ traverse_view (type& c)
{
// container.value = pointed-to.id
//
- semantics::data_member& id (*id_member (*vo->obj));
+ data_member_path& id (*id_member (*vo->obj));
c_cols->traverse (m, utype (id), "value", "value");
o_cols->traverse (id);
@@ -4994,7 +5024,7 @@ traverse_view (type& c)
{
// container.id = pointer.id
//
- semantics::data_member& id (*id_member (*e.vo->obj));
+ data_member_path& id (*id_member (*e.vo->obj));
c_cols->traverse (m, utype (id), "id", "object_id", e.vo->obj);
o_cols->traverse (id);
diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx
index e67931f..f495cef 100644
--- a/odb/relational/source.hxx
+++ b/odb/relational/source.hxx
@@ -211,7 +211,7 @@ namespace relational
semantics::class_& imc (
poly ? dynamic_cast<semantics::class_&> (imf.scope ()) : c);
- semantics::data_member& id (*id_member (imc));
+ data_member_path& id (*id_member (imc));
semantics::type& idt (utype (id));
if (container (imb))
@@ -484,7 +484,7 @@ namespace relational
instance<object_columns_list> l_cols; // Our id columns.
instance<object_columns_list> r_cols; // Other side id columns.
- semantics::data_member& id (*id_member (*us.obj));
+ data_member_path& id (*id_member (*us.obj));
l_cols->traverse (id);
@@ -1018,7 +1018,7 @@ namespace relational
dt = quote_id (table);
da = quote_id (poly ? alias + "_" + table.uname () : alias);
- semantics::data_member& id (*id_member (c));
+ data_member_path& id (*id_member (c));
instance<object_columns_list> oid_cols, cid_cols;
oid_cols->traverse (id);
@@ -1171,7 +1171,7 @@ namespace relational
bool query_;
size_t depth_;
string table_;
- semantics::data_member& id_;
+ data_member_path& id_;
instance<object_columns_list> id_cols_;
};
@@ -1391,7 +1391,7 @@ namespace relational
// Order of these tests is important.
//
- if (!insert_send_auto_id && id (mi.m) && auto_ (mi.m))
+ if (!insert_send_auto_id && auto_ (mi.m))
os << "if (sk != statement_insert && sk != statement_update)"
<< "{";
else if (section_ == 0 && separate_load (mi.m))
@@ -1567,7 +1567,7 @@ namespace relational
// The same logic as in pre().
//
- if (!insert_send_auto_id && id (mi.m) && auto_ (mi.m))
+ if (!insert_send_auto_id && auto_ (mi.m))
block = true;
else if (section_ == 0 && separate_load (mi.m))
block = true;
@@ -1673,7 +1673,7 @@ namespace relational
size_t insert (cc.total - cc.inverse - cc.optimistic_managed);
size_t update (insert - cc.id - cc.readonly - cc.separate_update);
- semantics::data_member* id;
+ data_member_path* id;
if (!insert_send_auto_id && (id = id_member (c)) != 0 && auto_ (*id))
insert -= cc.id;
@@ -2041,7 +2041,7 @@ namespace relational
// If we don't send auto id in INSERT statement, ignore this
// member altogether (we never send auto id in UPDATE).
//
- if (!insert_send_auto_id && id (mi.m) && auto_ (mi.m))
+ if (!insert_send_auto_id && auto_ (mi.m))
return false;
os << "// " << mi.m.name () << endl
@@ -2985,7 +2985,7 @@ namespace relational
bool poly_derived (poly && poly_root != &c);
size_t poly_depth (poly_derived ? polymorphic_depth (c) : 1);
- semantics::data_member* idm (id_member (poly ? *poly_root : c));
+ data_member_path* idm (id_member (poly ? *poly_root : c));
os << "// " << mi.m.name () << (pre_ ? " pre" : " post") << endl
<< "//" << endl;
@@ -3084,7 +3084,7 @@ namespace relational
for (size_t i (0); i < poly_depth - 1; ++i)
id_im += (i == 0 ? ".base" : "->base");
- string const& n (idm->name ());
+ string const& n (idm->front ()->name ());
id_var = id_im + (poly_derived ? "->" : ".") + n +
(n[n.size () - 1] == '_' ? "" : "_");
@@ -3563,7 +3563,7 @@ namespace relational
if (polymorphic (*c))
c = &dynamic_cast<semantics::class_&> (imf.scope ());
- semantics::data_member& inv_id (*id_member (*c));
+ data_member_path& inv_id (*id_member (*c));
qname inv_table; // Other table name.
string inv_qtable;
@@ -6456,7 +6456,7 @@ namespace relational
if (version (m))
p = version_value (m);
- else if (context::id (m) && auto_ (m)) // Only simple id can be auto.
+ else if (auto_ (m)) // Only simple, direct id can be auto.
p = qp_.auto_id ();
else
p = qp_.next ();
diff --git a/odb/relational/validator.cxx b/odb/relational/validator.cxx
index 5dea25f..f78c145 100644
--- a/odb/relational/validator.cxx
+++ b/odb/relational/validator.cxx
@@ -325,20 +325,20 @@ namespace relational
virtual void
traverse_object (type& c)
{
- semantics::data_member* id (id_member (c));
+ data_member_path* id (id_member (c));
if (id != 0)
{
if (semantics::class_* cm = composite_wrapper (utype (*id)))
{
+ location idl (id->front ()->location ());
+
// Composite id cannot be auto.
//
if (auto_ (*id))
{
- os << id->file () << ":" << id->line () << ":" << id->column ()
- << ": error: composite id cannot be automatically assigned"
- << endl;
-
+ error (idl) << "composite id cannot be automatically assigned"
+ << endl;
valid_ = false;
}
@@ -350,8 +350,7 @@ namespace relational
composite_id_members_.traverse (*cm);
if (!valid_)
- os << id->file () << ":" << id->line () << ":" << id->column ()
- << ": info: composite id is defined here" << endl;
+ info (idl) << "composite id is defined here" << endl;
}
// Check that the composite value type is default-constructible.
@@ -366,8 +365,7 @@ namespace relational
<< ": info: provide default constructor for this value type"
<< endl;
- os << id->file () << ":" << id->line () << ":" << id->column ()
- << ": info: composite id is defined here" << endl;
+ info (idl) << "composite id is defined here" << endl;
valid_ = false;
}
diff --git a/odb/semantics/elements.hxx b/odb/semantics/elements.hxx
index b52acc3..d0156dd 100644
--- a/odb/semantics/elements.hxx
+++ b/odb/semantics/elements.hxx
@@ -114,11 +114,12 @@ namespace semantics
public:
template <typename X>
- bool
- is_a () const
- {
- return dynamic_cast<X const*> (this) != 0;
- }
+ X*
+ is_a () {return dynamic_cast<X*> (this);}
+
+ template <typename X>
+ const X*
+ is_a () const {return dynamic_cast<const X*> (this);}
};
//
@@ -165,11 +166,12 @@ namespace semantics
public:
template <typename X>
- bool
- is_a () const
- {
- return dynamic_cast<X const*> (this) != 0;
- }
+ X*
+ is_a () {return dynamic_cast<X*> (this);}
+
+ template <typename X>
+ const X*
+ is_a () const {return dynamic_cast<const X*> (this);}
public:
node (path const& file, size_t line, size_t column, tree);
diff --git a/odb/validator.cxx b/odb/validator.cxx
index a76848f..c1d82c6 100644
--- a/odb/validator.cxx
+++ b/odb/validator.cxx
@@ -12,6 +12,7 @@
#include <odb/context.hxx>
#include <odb/diagnostics.hxx>
#include <odb/validator.hxx>
+#include <odb/cxx-lexer.hxx>
#include <odb/relational/validator.hxx>
@@ -346,7 +347,7 @@ namespace
virtual void
traverse (semantics::data_member& m)
{
- if (id (m))
+ if (m.count ("id"))
{
if (id_ == 0)
id_ = &m;
@@ -605,8 +606,10 @@ namespace
// Check special members.
//
- semantics::data_member* id (0);
- semantics::data_member* optimistic (0);
+ using semantics::data_member;
+
+ data_member* id (0);
+ data_member* optimistic (0);
{
special_members t (class_object, valid_, id, optimistic);
t.traverse (c);
@@ -636,39 +639,75 @@ namespace
}
else
{
- c.set ("id-member", id);
+ // Convert id to a member path. This has to happen early since
+ // a lot of code that runs next (e.g., processor, pass 1) depends
+ // on this information being available.
+ //
+ data_member_path& idp (c.set ("id-member", data_member_path ()));
+ idp.push_back (id);
+
+ // See if we have a member path that we need to resolve.
+ //
+ const string& name (id->get<string> ("id"));
+ location_t l (id->get<location_t> ("id-location"));
+
+ if (!name.empty ())
+ {
+ if (id->count ("auto"))
+ {
+ error (l) << "nested id cannot be automatically assigned" << endl;
+ valid_ = false;
+ }
+
+ if (semantics::class_* comp = utype (*id).is_a<semantics::class_> ())
+ {
+ try
+ {
+ resolve_data_members (idp, *comp, name, l, lex_);
+ }
+ catch (const operation_failed&) {valid_ = false;}
+ }
+ else
+ {
+ error (l) << "nested id requires composite member" << endl;
+ valid_ = false;
+ }
+
+ // Mark the whole member as readonly.
+ //
+ id->set ("readonly", true);
+ }
+
+ data_member* idf (idp.front ());
+ data_member* idb (idp.back ());
// Complain if an id member has a default value (default value
// for the id's type is ok -- we will ignore it).
//
- if (id->count ("default"))
+ if (idb->count ("default"))
{
- os << id->file () << ":" << id->line () << ":" << id->column ()
- << ": error: object id member cannot have default value" << endl;
+ error (l) << "object id member cannot have default value" << endl;
valid_ = false;
}
// Complain if an id member is in a section.
//
- if (id->count ("section-member"))
+ if (idf->count ("section-member"))
{
- os << id->file () << ":" << id->line () << ":" << id->column ()
- << ": error: object id member cannot be in a section" << endl;
+ error (l) << "object id member cannot be in a section" << endl;
valid_ = false;
}
// Automatically mark the id member as not null. If we already have
// an explicit null pragma for this member, issue an error.
//
- if (id->count ("null"))
+ if (idb->count ("null"))
{
- os << id->file () << ":" << id->line () << ":" << id->column ()
- << ": error: object id member cannot be null" << endl;
-
+ error (l) << "object id member cannot be null" << endl;
valid_ = false;
}
else
- id->set ("not-null", true);
+ idf->set ("not-null", true);
}
if (optimistic != 0)
@@ -1021,6 +1060,8 @@ namespace
value_type vt_;
data_member1 member_;
traversal::names names_member_;
+
+ cxx_string_lexer lex_;
};
//
@@ -1080,7 +1121,7 @@ namespace
// Make sure the pointed-to class has object id unless it is in a
// view where we can load no-id objects.
//
- if (semantics::data_member* id = id_member (*c))
+ if (data_member_path* id = id_member (*c))
{
semantics::type& idt (utype (*id));
@@ -1437,7 +1478,7 @@ namespace
}
}
- if (semantics::data_member* id = id_member (c))
+ if (data_member_path* id = id_member (c))
{
semantics::type& t (utype (*id));
@@ -1478,6 +1519,8 @@ namespace
if (!v)
{
+ semantics::data_member& idm (*id->front ());
+
os << t.file () << ":" << t.line () << ":" << t.column ()
<< ": error: value type that is used as object id in "
<< "persistent class with session support does not define "
@@ -1486,7 +1529,7 @@ namespace
os << t.file () << ":" << t.line () << ":" << t.column ()
<< ": info: provide operator< for this value type" << endl;
- os << id->file () << ":" << id->line () << ":" << id->column ()
+ os << idm.file () << ":" << idm.line () << ":" << idm.column ()
<< ": info: id member is defined here" << endl;
os << c.file () << ":" << c.line () << ":" << c.column ()