From 759c865ebbdf8401e58ef5df705f9d1ad3d83cc9 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 22 Nov 2010 14:18:40 +0200 Subject: Implement support for one-to-{one,many} inverse relationships New pragma: inverse. --- odb/common.hxx | 5 +- odb/context.cxx | 5 +- odb/context.hxx | 18 ++++- odb/mysql/common.cxx | 36 +++++---- odb/mysql/common.hxx | 8 +- odb/mysql/header.cxx | 6 +- odb/mysql/schema.cxx | 9 ++- odb/mysql/source.cxx | 204 ++++++++++++++++++++++++++++++++++++++----------- odb/parser.cxx | 2 + odb/pragma.cxx | 43 +++++++++++ odb/type-processor.cxx | 47 ++++++++++-- 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 (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 ("inverse", 0) + : m.get (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 (file, line, clmn, d)); + unit_->insert (d, member_node); unit_->new_edge (*c_node, member_node, name, a); belongs& edge (unit_->new_edge (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 (file, line, clmn, d)); + unit_->insert (d, member_node); unit_->new_edge (*u_node, member_node, name, a); belongs& edge (unit_->new_edge (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 (unit.find (tn))) + class_* c (dynamic_cast (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 ("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 (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 -- cgit v1.1