summaryrefslogtreecommitdiff
path: root/odb
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2010-11-22 14:18:40 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2010-11-22 14:18:40 +0200
commit759c865ebbdf8401e58ef5df705f9d1ad3d83cc9 (patch)
tree142b8b4f0ef70774632ae6e9254b9514ccbc8d29 /odb
parent4a50c5a7e9976587569276c768f85ad481694e70 (diff)
Implement support for one-to-{one,many} inverse relationships
New pragma: inverse.
Diffstat (limited to 'odb')
-rw-r--r--odb/common.hxx5
-rw-r--r--odb/context.cxx5
-rw-r--r--odb/context.hxx18
-rw-r--r--odb/mysql/common.cxx36
-rw-r--r--odb/mysql/common.hxx8
-rw-r--r--odb/mysql/header.cxx6
-rw-r--r--odb/mysql/schema.cxx9
-rw-r--r--odb/mysql/source.cxx204
-rw-r--r--odb/parser.cxx2
-rw-r--r--odb/pragma.cxx43
-rw-r--r--odb/type-processor.cxx47
11 files changed, 313 insertions, 70 deletions
diff --git a/odb/common.hxx b/odb/common.hxx
index 3811ea9..96d1330 100644
--- a/odb/common.hxx
+++ b/odb/common.hxx
@@ -90,7 +90,10 @@ private:
//
struct object_columns_base: traversal::class_
{
- virtual void
+ // Returning false means that the column has been ignored and the
+ // first flag should not be changed.
+ //
+ virtual bool
column (semantics::data_member&, std::string const& name, bool first) = 0;
// If you override this function, always call the base. The second argument
diff --git a/odb/context.cxx b/odb/context.cxx
index 436b60f..d0784c2 100644
--- a/odb/context.cxx
+++ b/odb/context.cxx
@@ -326,9 +326,10 @@ namespace
}
virtual void
- simple (semantics::data_member&)
+ simple (semantics::data_member& m)
{
- count_++;
+ if (out_ || !context::inverse (m))
+ count_++;
}
private:
diff --git a/odb/context.hxx b/odb/context.hxx
index 4d6dd10..8878e54 100644
--- a/odb/context.hxx
+++ b/odb/context.hxx
@@ -75,7 +75,8 @@ public:
}
static semantics::class_*
- object_pointer (semantics::data_member& m, string const& key_prefix)
+ object_pointer (semantics::data_member& m,
+ string const& key_prefix = string ())
{
using semantics::class_;
@@ -84,6 +85,18 @@ public:
: m.get<class_*> (key_prefix + "-object-pointer", 0);
}
+ static semantics::data_member*
+ inverse (semantics::data_member& m, string const& key_prefix = string ())
+ {
+ using semantics::data_member;
+
+ return object_pointer (m, key_prefix)
+ ? (key_prefix.empty ()
+ ? m.get<data_member*> ("inverse", 0)
+ : m.get<data_member*> (key_prefix + "-inverse", 0))
+ : 0;
+ }
+
// Database names and types.
//
public:
@@ -94,6 +107,9 @@ public:
//
struct table_prefix
{
+ table_prefix () {}
+ table_prefix (string const& p, size_t l): prefix (p), level (l) {}
+
string prefix;
size_t level;
};
diff --git a/odb/mysql/common.cxx b/odb/mysql/common.cxx
index d7e6187..e33e7df 100644
--- a/odb/mysql/common.cxx
+++ b/odb/mysql/common.cxx
@@ -36,16 +36,20 @@ namespace mysql
if (comp_value (t))
{
member_info mi (m, t, var, fq_type_override_);
- pre (mi);
- traverse_composite (mi);
- post (mi);
+ if (pre (mi))
+ {
+ traverse_composite (mi);
+ post (mi);
+ }
}
else if (container (t))
{
member_info mi (m, t, var, fq_type_override_);
- pre (mi);
- traverse_container (mi);
- post (mi);
+ if (pre (mi))
+ {
+ traverse_container (mi);
+ post (mi);
+ }
}
else
{
@@ -55,17 +59,21 @@ namespace mysql
{
member_info mi (m, id_member (*c).type (), var, fq_type_override_);
mi.st = &st;
- pre (mi);
- traverse_object_pointer (mi);
- post (mi);
+ if (pre (mi))
+ {
+ traverse_object_pointer (mi);
+ post (mi);
+ }
}
else
{
member_info mi (m, t, var, fq_type_override_);
mi.st = &st;
- pre (mi);
- traverse_simple (mi);
- post (mi);
+ if (pre (mi))
+ {
+ traverse_simple (mi);
+ post (mi);
+ }
}
}
}
@@ -475,7 +483,7 @@ namespace mysql
}
}
- void query_columns::
+ bool query_columns::
column (semantics::data_member& m, string const& col_name, bool)
{
string name (public_name (m));
@@ -509,5 +517,7 @@ namespace mysql
<< column << ");"
<< endl;
}
+
+ return true;
}
}
diff --git a/odb/mysql/common.hxx b/odb/mysql/common.hxx
index fb1f2a3..3044aa9 100644
--- a/odb/mysql/common.hxx
+++ b/odb/mysql/common.hxx
@@ -66,9 +66,13 @@ namespace mysql
}
};
- virtual void
+ // The false return value indicates that no further callbacks
+ // should be called for this member.
+ //
+ virtual bool
pre (member_info&)
{
+ return true;
}
virtual void
@@ -248,7 +252,7 @@ namespace mysql
virtual void
composite (semantics::data_member&, semantics::type&);
- virtual void
+ virtual bool
column (semantics::data_member&, string const&, bool);
private:
diff --git a/odb/mysql/header.cxx b/odb/mysql/header.cxx
index 1dc080a..4d604a4 100644
--- a/odb/mysql/header.cxx
+++ b/odb/mysql/header.cxx
@@ -29,17 +29,19 @@ namespace mysql
{
}
- virtual void
+ virtual bool
pre (member_info& mi)
{
if (container (mi.t))
- return;
+ return false;
image_type = member_image_type_.image_type (mi.m);
if (var_override_.empty ())
os << "// " << mi.m.name () << endl
<< "//" << endl;
+
+ return true;
}
virtual void
diff --git a/odb/mysql/schema.cxx b/odb/mysql/schema.cxx
index 0c004ad..0007121 100644
--- a/odb/mysql/schema.cxx
+++ b/odb/mysql/schema.cxx
@@ -23,9 +23,14 @@ namespace mysql
{
}
- virtual void
+ virtual bool
column (semantics::data_member& m, string const& name, bool first)
{
+ // Ignore inverse object pointers.
+ //
+ if (inverse (m))
+ return false;
+
if (!first)
os << "," << endl;
@@ -40,6 +45,8 @@ namespace mysql
os << " REFERENCES `" << table_name (*c) << "` (`" <<
column_name (id_member (*c)) << "`)";
}
+
+ return true;
}
private:
diff --git a/odb/mysql/source.cxx b/odb/mysql/source.cxx
index 1588e36..bdcae1b 100644
--- a/odb/mysql/source.cxx
+++ b/odb/mysql/source.cxx
@@ -18,36 +18,130 @@ namespace mysql
{
struct object_columns: object_columns_base, context
{
- object_columns (context& c, char const* suffix = "")
+ object_columns (context& c,
+ std::string const& table_name,
+ bool out,
+ char const* suffix = "")
: object_columns_base (c),
context (c),
+ table_name_ (table_name),
+ out_ (out),
first_ (true),
suffix_ (suffix)
{
}
- object_columns (context& c, bool first, char const* suffix = "")
+ object_columns (context& c,
+ std::string const& table_name,
+ bool out,
+ bool first,
+ char const* suffix = "")
: object_columns_base (c),
context (c),
+ table_name_ (table_name),
+ out_ (out),
first_ (first),
suffix_ (suffix)
{
}
- virtual void
- column (semantics::data_member&, string const& name, bool first)
+ virtual bool
+ column (semantics::data_member& m, string const& name, bool first)
{
+ semantics::data_member* im (inverse (m));
+
+ // Ignore inverse object pointers if we are generating 'in' columns.
+ //
+ if (im != 0 && !out_)
+ return false;
+
if (!first || !first_)
os << ",\"" << endl;
- os << "\"`" << name << "`" << suffix_;
+ // Inverse object pointers come from a joined table.
+ //
+ if (im != 0)
+ {
+ semantics::class_* c (object_pointer (m));
+
+ if (container (im->type ()))
+ {
+ // This container is a direct member of the class so the table
+ // prefix is just the class table name.
+ //
+ table_prefix tp (table_name (*c) + "_", 1);
+ string const& it (table_name (*im, tp));
+ string const& id (column_name (*im, "id", "object_id"));
+
+ os << "\"`" << it << "`.`" << id << "`" << suffix_;
+ }
+ else
+ {
+ os << "\"`" << table_name (*c) << "`.`" <<
+ column_name (id_member (*c)) << "`" << suffix_;
+ }
+ }
+ else
+ os << "\"`" << table_name_ << "`.`" << name << "`" << suffix_;
+
+ return true;
}
private:
+ string table_name_;
+ bool out_;
bool first_;
string suffix_;
};
+ struct object_joins: object_columns_base, context
+ {
+ object_joins (context& c, semantics::class_& scope)
+ : object_columns_base (c),
+ context (c),
+ table_ (table_name (scope)),
+ id_ (id_member (scope))
+ {
+ }
+
+ virtual bool
+ column (semantics::data_member& m, string const&, bool)
+ {
+ if (semantics::data_member* im = inverse (m))
+ {
+ semantics::class_* c (object_pointer (m));
+
+ if (container (im->type ()))
+ {
+ // This container is a direct member of the class so the table
+ // prefix is just the class table name.
+ //
+ table_prefix tp (table_name (*c) + "_", 1);
+ string const& it (table_name (*im, tp));
+ string const& val (column_name (*im, "value", "value"));
+
+ os << "\" LEFT JOIN `" << it << "` ON `" << it << "`.`" <<
+ val << "` = `" << table_ << "`.`" <<
+ column_name (id_) << "`\"" << endl;
+ }
+ else
+ {
+ string const& it (table_name (*c));
+
+ os << "\" LEFT JOIN `" << it << "` ON `" << it << "`.`" <<
+ column_name (*im) << "` = `" << table_ << "`.`" <<
+ column_name (id_) << "`\"" << endl;
+ }
+ }
+
+ return true;
+ }
+
+ private:
+ string table_;
+ semantics::data_member& id_;
+ };
+
const char* integer_buffer_types[] =
{
"MYSQL_TYPE_TINY",
@@ -111,11 +205,11 @@ namespace mysql
{
}
- virtual void
+ virtual bool
pre (member_info& mi)
{
if (container (mi.t))
- return;
+ return false;
ostringstream ostr;
ostr << "b[n]";
@@ -124,16 +218,21 @@ namespace mysql
arg = arg_override_.empty () ? string ("i") : arg_override_;
if (var_override_.empty ())
+ {
os << "// " << mi.m.name () << endl
<< "//" << endl;
+
+ if (inverse (mi.m, key_prefix_))
+ os << "if (out)"
+ << "{";
+ }
+
+ return true;
}
virtual void
post (member_info& mi)
{
- if (container (mi.t))
- return;
-
if (var_override_.empty ())
{
if (semantics::class_* c = comp_value (mi.t))
@@ -141,7 +240,10 @@ namespace mysql
else
os << "n++;";
- os << endl;
+ if (inverse (mi.m, key_prefix_))
+ os << "}";
+ else
+ os << endl;
}
}
@@ -314,11 +416,11 @@ namespace mysql
{
}
- virtual void
+ virtual bool
pre (member_info& mi)
{
if (container (mi.t))
- return;
+ return false;
ostringstream ostr;
ostr << "e[" << index_ << "UL]";
@@ -327,14 +429,14 @@ namespace mysql
if (var_override_.empty ())
os << "// " << mi.m.name () << endl
<< "//" << endl;
+
+ return true;
}
virtual void
post (member_info& mi)
{
- if (container (mi.t))
- return;
- else if (semantics::class_* c = comp_value (mi.t))
+ if (semantics::class_* c = comp_value (mi.t))
index_ += in_column_count (*c);
else
index_++;
@@ -496,11 +598,14 @@ namespace mysql
{
}
- virtual void
+ virtual bool
pre (member_info& mi)
{
- if (container (mi.t))
- return;
+ // Ignore containers (they get their own table) and inverse
+ // object pointers (they are not present in the 'in' binding).
+ //
+ if (container (mi.t) || inverse (mi.m, key_prefix_))
+ return false;
if (!member_override_.empty ())
member = member_override_;
@@ -555,14 +660,13 @@ namespace mysql
+ image_type + ",\n "
+ db_type + " >";
}
+
+ return true;
}
virtual void
post (member_info& mi)
{
- if (container (mi.t))
- return;
-
if (!comp_value (mi.t))
{
if (object_pointer (mi.m, key_prefix_))
@@ -757,11 +861,11 @@ namespace mysql
{
}
- virtual void
+ virtual bool
pre (member_info& mi)
{
if (container (mi.t))
- return;
+ return false;
if (!member_override_.empty ())
member = member_override_;
@@ -812,14 +916,13 @@ namespace mysql
+ image_type + ",\n "
+ db_type + " >";
}
+
+ return true;
}
virtual void
post (member_info& mi)
{
- if (container (mi.t))
- return;
-
if (!comp_value (mi.t) && object_pointer (mi.m, key_prefix_))
{
member = member_override_.empty ()
@@ -1060,7 +1163,7 @@ namespace mysql
{
if (comp_value (*kt))
{
- object_columns t (*this, false);
+ object_columns t (*this, table, false, false);
t.traverse_composite (m, *kt, "key", "key");
}
else
@@ -1079,7 +1182,7 @@ namespace mysql
if (comp_value (vt))
{
- object_columns t (*this, false);
+ object_columns t (*this, table, false, false);
t.traverse_composite (m, vt, "value", "value");
}
else
@@ -1116,7 +1219,7 @@ namespace mysql
{
if (comp_value (*kt))
{
- object_columns t (*this, false);
+ object_columns t (*this, table, false, false);
t.traverse_composite (m, *kt, "key", "key");
}
else
@@ -1135,7 +1238,7 @@ namespace mysql
if (comp_value (vt))
{
- object_columns t (*this, false);
+ object_columns t (*this, table, false, false);
t.traverse_composite (m, vt, "value", "value");
}
else
@@ -2008,13 +2111,15 @@ namespace mysql
t.traverse (c);
}
+ string const& table (table_name (c));
+
// persist_statement
//
os << "const char* const " << traits << "::persist_statement =" << endl
- << "\"INSERT INTO `" << table_name (c) << "` (\"" << endl;
+ << "\"INSERT INTO `" << table << "` (\"" << endl;
{
- object_columns t (*this);
+ object_columns t (*this, table, false);
t.traverse (c);
}
@@ -2033,34 +2138,40 @@ namespace mysql
<< "\"SELECT \"" << endl;
{
- object_columns t (*this);
+ object_columns t (*this, table, true);
t.traverse (c);
}
os << "\"" << endl
- << "\" FROM `" << table_name (c) << "` WHERE `" <<
- column_name (id) << "` = ?\";"
+ << "\" FROM `" << table << "`\"" << endl;
+
+ {
+ object_joins t (*this, c);
+ t.traverse (c);
+ }
+
+ os << "\" WHERE `" << table << "`.`" << column_name (id) << "` = ?\";"
<< endl;
// update_statement
//
os << "const char* const " << traits << "::update_statement =" << endl
- << "\"UPDATE `" << table_name (c) << "` SET \"" << endl;
+ << "\"UPDATE `" << table << "` SET \"" << endl;
{
- object_columns t (*this, " = ?");
+ object_columns t (*this, table, false, " = ?");
t.traverse (c);
}
os << "\"" << endl
- << "\" WHERE `" << column_name (id) << "` = ?\";"
+ << "\" WHERE `" << table << "`.`" << column_name (id) << "` = ?\";"
<< endl;
// erase_statement
//
os << "const char* const " << traits << "::erase_statement =" << endl
- << "\"DELETE FROM `" << table_name (c) << "`\"" << endl
- << "\" WHERE `" << column_name (id) << "` = ?\";"
+ << "\"DELETE FROM `" << table << "`\"" << endl
+ << "\" WHERE `" << table << "`.`" << column_name (id) << "` = ?\";"
<< endl;
// query_clause
@@ -2071,12 +2182,19 @@ namespace mysql
<< "\"SELECT \"" << endl;
{
- object_columns t (*this);
+ object_columns t (*this, table, true);
t.traverse (c);
}
os << "\"" << endl
- << "\" FROM `" << table_name (c) << "` \";"
+ << "\" FROM `" << table << "`\"" << endl;
+
+ {
+ object_joins t (*this, c);
+ t.traverse (c);
+ }
+
+ os << "\" \";"
<< endl;
}
diff --git a/odb/parser.cxx b/odb/parser.cxx
index 0598444..88c32d6 100644
--- a/odb/parser.cxx
+++ b/odb/parser.cxx
@@ -409,6 +409,7 @@ emit_class (tree c, path const& file, size_t line, size_t clmn, bool stub)
type& type_node (emit_type (t, file, line, clmn));
data_member& member_node (
unit_->new_node<data_member> (file, line, clmn, d));
+ unit_->insert (d, member_node);
unit_->new_edge<names> (*c_node, member_node, name, a);
belongs& edge (unit_->new_edge<belongs> (member_node, type_node));
@@ -562,6 +563,7 @@ emit_union (tree u, path const& file, size_t line, size_t clmn, bool stub)
type& type_node (emit_type (t, file, line, clmn));
data_member& member_node (
unit_->new_node<data_member> (file, line, clmn, d));
+ unit_->insert (d, member_node);
unit_->new_edge<names> (*u_node, member_node, name, a);
belongs& edge (unit_->new_edge<belongs> (member_node, type_node));
diff --git a/odb/pragma.cxx b/odb/pragma.cxx
index adcf06e..ddb7d15 100644
--- a/odb/pragma.cxx
+++ b/odb/pragma.cxx
@@ -128,6 +128,7 @@ check_decl_type (tree d, string const& name, string const& p, location_t l)
p == "index_column" ||
p == "key_column" ||
p == "id_column" ||
+ p == "inverse" ||
p == "transient")
{
if (tc != FIELD_DECL)
@@ -424,6 +425,40 @@ handle_pragma (cpp_reader* reader,
tt = pragma_lex (&t);
}
+ else if (p == "inverse")
+ {
+ // inverse (name)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl != 0 && !check_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (pragma_lex (&t) != CPP_OPEN_PAREN)
+ {
+ error ("%qs expected after db pragma %qs", "(", pc);
+ return;
+ }
+
+ tt = pragma_lex (&t);
+
+ if (tt != CPP_NAME)
+ {
+ error ("member name expected in db pragma %qs", pc);
+ return;
+ }
+
+ val = IDENTIFIER_POINTER (t);
+
+ if (pragma_lex (&t) != CPP_CLOSE_PAREN)
+ {
+ error ("%qs expected at the end of db pragma %qs", ")", pc);
+ return;
+ }
+
+ tt = pragma_lex (&t);
+ }
else if (p == "transient")
{
// transient
@@ -611,6 +646,7 @@ handle_pragma_qualifier (cpp_reader* reader, string const& p)
p == "index_type" ||
p == "key_type" ||
p == "table" ||
+ p == "inverse" ||
p == "transient")
{
handle_pragma (reader, p, 0, "");
@@ -739,6 +775,12 @@ handle_pragma_db_table (cpp_reader* reader)
}
extern "C" void
+handle_pragma_db_inverse (cpp_reader* reader)
+{
+ handle_pragma_qualifier (reader, "inverse");
+}
+
+extern "C" void
handle_pragma_db_transient (cpp_reader* reader)
{
handle_pragma_qualifier (reader, "transient");
@@ -762,5 +804,6 @@ register_odb_pragmas (void*, void*)
c_register_pragma_with_expansion ("db", "index_type", handle_pragma_db_itype);
c_register_pragma_with_expansion ("db", "key_type", handle_pragma_db_ktype);
c_register_pragma_with_expansion ("db", "table", handle_pragma_db_table);
+ c_register_pragma_with_expansion ("db", "inverse", handle_pragma_db_inverse);
c_register_pragma_with_expansion ("db", "transient", handle_pragma_db_transient);
}
diff --git a/odb/type-processor.cxx b/odb/type-processor.cxx
index 9de59b4..fcdb3eb 100644
--- a/odb/type-processor.cxx
+++ b/odb/type-processor.cxx
@@ -421,6 +421,7 @@ namespace
// element type and see if it is an object.
//
using semantics::class_;
+ using semantics::data_member;
tree inst (instantiate_template (pointer_traits_, t.tree_node ()));
@@ -449,16 +450,52 @@ namespace
throw;
}
- if (class_* c = dynamic_cast<class_*> (unit.find (tn)))
+ class_* c (dynamic_cast<class_*> (unit.find (tn)));
+
+ if (c == 0 || !c->count ("object"))
+ return 0;
+
+ m.set (kp + (kp.empty () ? "": "-") + "object-pointer", c);
+
+ // See if this is the inverse side of a bidirectional relationship.
+ // If so, then resolve the member and cache it in the context.
+ //
+ if (m.count ("inverse"))
{
- if (c->count ("object"))
+ string name (m.get<string> ("inverse"));
+ tree decl (
+ lookup_qualified_name (
+ tn, get_identifier (name.c_str ()), false, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != FIELD_DECL)
{
- m.set (kp + (kp.empty () ? "": "-") + "object-pointer", c);
- return c;
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": "
+ << "error: unable to resolve data member '" << name << "' "
+ << "specified with '#pragma db inverse' in class '"
+ << c->fq_name () << "'" << endl;
+ throw generation_failed ();
+ }
+
+ data_member* im (dynamic_cast<data_member*> (unit.find (decl)));
+
+ if (im == 0)
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": "
+ << "ice: unable to find semantic graph node corresponding to "
+ << "data member '" << name << "' in class '" << c->fq_name ()
+ << "'" << endl;
+ throw generation_failed ();
}
+
+ // @@ Would be good to check that the other end is actually
+ // an object pointer and is not marked as inverse. But the
+ // other class may not have been processed yet.
+ //
+ m.remove ("inverse");
+ m.set (kp + (kp.empty () ? "": "-") + "inverse", im);
}
- return 0;
+ return c;
}
tree