From 1f3fcd13efb06a49bc7363fb58dd1f8d41a069ee Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 25 Nov 2010 11:19:03 +0200 Subject: Add support for object pointers in query --- odb/common.cxx | 26 +++---- odb/common.hxx | 8 +-- odb/mysql/common.cxx | 97 ++++++++++++++++++-------- odb/mysql/common.hxx | 6 +- odb/mysql/schema.cxx | 8 +-- odb/mysql/source.cxx | 192 ++++++++++++++++++++++++++++++++++++++++++--------- 6 files changed, 257 insertions(+), 80 deletions(-) diff --git a/odb/common.cxx b/odb/common.cxx index 992b34b..1c1b067 100644 --- a/odb/common.cxx +++ b/odb/common.cxx @@ -17,9 +17,10 @@ simple (semantics::data_member&) } void object_members_base:: -composite (semantics::data_member&, semantics::type& t) +composite (semantics::data_member&, semantics::class_& c) { - dispatch (t); + inherits (c); + names (c); } void object_members_base:: @@ -28,9 +29,9 @@ container (semantics::data_member&) } void object_members_base:: -traverse_composite (semantics::data_member& m, semantics::type& t) +traverse_composite (semantics::data_member& m, semantics::class_& c) { - composite (m, t); + composite (m, c); } void object_members_base:: @@ -63,7 +64,7 @@ traverse (semantics::data_member& m) semantics::type& t (m.type ()); - if (context::comp_value (t)) + if (semantics::class_* comp = context::comp_value (t)) { string old_prefix, old_table_prefix; @@ -105,7 +106,7 @@ traverse (semantics::data_member& m) om_.table_prefix_.level++; } - om_.composite (m, t); + om_.composite (m, *comp); if (om_.build_table_prefix_) { @@ -131,14 +132,15 @@ traverse (semantics::data_member& m) // void object_columns_base:: -composite (semantics::data_member&, semantics::type& t) +composite (semantics::data_member&, semantics::class_& c) { - dispatch (t); + inherits (c); + names (c); } void object_columns_base:: traverse_composite (semantics::data_member& m, - semantics::type& t, + semantics::class_& c, string const& key_prefix, string const& default_name) { @@ -158,7 +160,7 @@ traverse_composite (semantics::data_member& m, member_.prefix_ += '_'; } - composite (m, t); + composite (m, c); } void object_columns_base:: @@ -176,7 +178,7 @@ traverse (semantics::data_member& m) semantics::type& t (m.type ()); - if (comp_value (t)) + if (semantics::class_* comp = comp_value (t)) { string old_prefix (prefix_); @@ -196,7 +198,7 @@ traverse (semantics::data_member& m) prefix_ += '_'; } - oc_.composite (m, t); + oc_.composite (m, *comp); prefix_ = old_prefix; } diff --git a/odb/common.hxx b/odb/common.hxx index 96d1330..775f009 100644 --- a/odb/common.hxx +++ b/odb/common.hxx @@ -24,7 +24,7 @@ struct object_members_base: traversal::class_ // traverse_composite(). // virtual void - composite (semantics::data_member& m, semantics::type& t); + composite (semantics::data_member&, semantics::class_&); virtual void container (semantics::data_member&); @@ -56,7 +56,7 @@ public: traverse (semantics::class_&); virtual void - traverse_composite (semantics::data_member&, semantics::type&); + traverse_composite (semantics::data_member&, semantics::class_&); protected: std::string prefix_; @@ -101,7 +101,7 @@ struct object_columns_base: traversal::class_ // m.type (). // virtual void - composite (semantics::data_member& m, semantics::type& t); + composite (semantics::data_member&, semantics::class_&); public: object_columns_base (context& c) @@ -116,7 +116,7 @@ public: virtual void traverse_composite (semantics::data_member&, - semantics::type&, + semantics::class_&, std::string const& key_prefix, std::string const& default_name); diff --git a/odb/mysql/common.cxx b/odb/mysql/common.cxx index e33e7df..3cec95c 100644 --- a/odb/mysql/common.cxx +++ b/odb/mysql/common.cxx @@ -438,6 +438,7 @@ namespace mysql query_columns (context& c) : object_columns_base (c), context (c), + ptr_ (true), decl_ (true), member_image_type_ (c), member_database_type_ (c) @@ -448,6 +449,7 @@ namespace mysql query_columns (context& c, semantics::class_& cl) : object_columns_base (c), context (c), + ptr_ (true), decl_ (false), member_image_type_ (c), member_database_type_ (c) @@ -457,7 +459,7 @@ namespace mysql } void query_columns:: - composite (semantics::data_member& m, semantics::type& t) + composite (semantics::data_member& m, semantics::class_& c) { string name (public_name (m)); @@ -468,7 +470,7 @@ namespace mysql << "struct " << name << "{"; - object_columns_base::composite (m, t); + object_columns_base::composite (m, c); os << "};"; } @@ -477,7 +479,7 @@ namespace mysql string old_scope (scope_); scope_ += "::" + name; - object_columns_base::composite (m, t); + object_columns_base::composite (m, c); scope_ = old_scope; } @@ -487,35 +489,76 @@ namespace mysql column (semantics::data_member& m, string const& col_name, bool) { string name (public_name (m)); - string db_type (member_database_type_.database_type (m)); - string type ( - "mysql::value_traits< " - + m.type ().fq_name (m.belongs ().hint ()) + ", " - + member_image_type_.image_type (m) + ", " - + db_type - + " >::query_type"); - - if (decl_) + if (semantics::class_* c = object_pointer (m)) { - os << "// " << name << endl - << "//" << endl - << "static const mysql::query_column<" << endl - << " " << type << "," << endl - << " " << db_type << ">" << endl - << name << ";" - << endl; + // We cannot just typedef the query_type from the referenced + // object for two reasons: (1) it may not be defined yet and + // (2) it will contain columns for its own pointers which + // won't work (for now we only support one level of indirection + // in queries). So we will have to duplicate the columns (sans + // the pointers). + // + if (ptr_) + { + ptr_ = false; + + if (decl_) + { + os << "// " << name << endl + << "//" << endl + << "struct " << name + << "{"; + + traverse (*c); + + os << "};"; + } + else + { + string old_scope (scope_), old_table (table_); + scope_ += "::" + name; + table_ = table_name (*c); + traverse (*c); + table_ = old_table; + scope_ = old_scope; + } + + ptr_ = true; + } } else { - string column ("\"`" + table_ + "`.`" + col_name + "`\""); - - os << "const mysql::query_column<" << endl - << " " << type << "," << endl - << " " << db_type << ">" << endl - << scope_ << "::" << name << " (" << endl - << column << ");" - << endl; + string db_type (member_database_type_.database_type (m)); + + string type ( + "mysql::value_traits< " + + m.type ().fq_name (m.belongs ().hint ()) + ", " + + member_image_type_.image_type (m) + ", " + + db_type + + " >::query_type"); + + if (decl_) + { + os << "// " << name << endl + << "//" << endl + << "static const mysql::query_column<" << endl + << " " << type << "," << endl + << " " << db_type << ">" << endl + << name << ";" + << endl; + } + else + { + string column ("\"`" + table_ + "`.`" + col_name + "`\""); + + os << "const mysql::query_column<" << endl + << " " << type << "," << endl + << " " << db_type << ">" << endl + << scope_ << "::" << name << " (" << endl + << column << ");" + << endl; + } } return true; diff --git a/odb/mysql/common.hxx b/odb/mysql/common.hxx index 3044aa9..4e9d427 100644 --- a/odb/mysql/common.hxx +++ b/odb/mysql/common.hxx @@ -250,15 +250,17 @@ namespace mysql query_columns (context&, semantics::class_&); virtual void - composite (semantics::data_member&, semantics::type&); + composite (semantics::data_member&, semantics::class_&); virtual bool column (semantics::data_member&, string const&, bool); private: + bool ptr_; + bool decl_; + string scope_; string table_; - bool decl_; member_image_type member_image_type_; member_database_type member_database_type_; diff --git a/odb/mysql/schema.cxx b/odb/mysql/schema.cxx index 0a26e65..cb89c4b 100644 --- a/odb/mysql/schema.cxx +++ b/odb/mysql/schema.cxx @@ -111,10 +111,10 @@ namespace mysql os << "," << endl; - if (comp_value (kt)) + if (semantics::class_* ckt = comp_value (kt)) { object_columns oc (*this); - oc.traverse_composite (m, kt, "key", "key"); + oc.traverse_composite (m, *ckt, "key", "key"); } else { @@ -129,10 +129,10 @@ namespace mysql { os << "," << endl; - if (comp_value (vt)) + if (semantics::class_* cvt = comp_value (vt)) { object_columns oc (*this); - oc.traverse_composite (m, vt, "value", "value"); + oc.traverse_composite (m, *cvt, "value", "value"); } else { diff --git a/odb/mysql/source.cxx b/odb/mysql/source.cxx index c20c6c4..6f95623 100644 --- a/odb/mysql/source.cxx +++ b/odb/mysql/source.cxx @@ -5,6 +5,9 @@ #include +#include +#include +#include #include #include @@ -96,50 +99,173 @@ namespace mysql struct object_joins: object_columns_base, context { - object_joins (context& c, semantics::class_& scope) + object_joins (context& c, semantics::class_& scope, bool query) : object_columns_base (c), context (c), + query_ (query), table_ (table_name (scope)), id_ (id_member (scope)) { } + size_t + count () const + { + return joins_.size (); + } + + void + write () + { + for (joins::iterator i (joins_.begin ()); i != joins_.end (); ++i) + { + if (i->table.empty ()) + continue; + + os << "\" LEFT JOIN `" << i->table << "` ON "; + + for (conditions::iterator b (i->cond.begin ()), j (b); + j != i->cond.end (); ++j) + { + if (j != b) + os << " OR "; + + os << *j; + } + + os << "\"" << endl; + } + } + virtual bool - column (semantics::data_member& m, string const&, bool) + column (semantics::data_member& m, string const& col_name, bool) { + semantics::class_* c (object_pointer (m)); + + if (c == 0) + return true; + + string t, dt; + ostringstream cond, dcond; + 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& ct (table_name (*c)); + table_prefix tp (ct + "_", 1); + t = table_name (*im, tp); string const& val (column_name (*im, "value", "value")); - os << "\" LEFT JOIN `" << it << "` ON `" << it << "`.`" << - val << "` = `" << table_ << "`.`" << - column_name (id_) << "`\"" << endl; + cond << "`" << t << "`.`" << val << "` = `" << + table_ << "`.`" << column_name (id_) << "`"; + + // Add the join for the object itself so that we are able to + // use it in the WHERE clause. + // + if (query_) + { + dt = ct; + string const& id (column_name (*im, "id", "object_id")); + + dcond << "`" << dt << "`.`" << column_name (id_member (*c)) << + "` = `" << t << "`.`" << id << "`"; + } + } + else + { + t = table_name (*c); + + cond << "`" << t << "`.`" << column_name (*im) << "` = `" << + table_ << "`.`" << column_name (id_) << "`"; } + } + else if (query_) + { + // We need the join to be able to use the referenced object + // in the WHERE clause. + // + t = table_name (*c); + + cond << "`" << t << "`.`" << column_name (id_member (*c)) << + "` = `" << table_ << "`.`" << col_name << "`"; + } + + if (!t.empty ()) + { + size_t i; + table_map::iterator it (table_map_.find (t)); + + if (it != table_map_.end ()) + i = it->second; else { - string const& it (table_name (*c)); + i = joins_.size (); + joins_.push_back (join ()); + table_map_[t] = i; + } + + joins_[i].table = t; + joins_[i].cond.insert (cond.str ()); + } + + if (!dt.empty ()) + { + // Add dependent join. If one already exists, move it to the + // bottom. + // + size_t i; + table_map::iterator it (table_map_.find (dt)); - os << "\" LEFT JOIN `" << it << "` ON `" << it << "`.`" << - column_name (*im) << "` = `" << table_ << "`.`" << - column_name (id_) << "`\"" << endl; + if (it != table_map_.end ()) + { + i = joins_.size (); + joins_.push_back (join ()); + joins_[it->second].swap (joins_.back ()); + it->second = i; + } + else + { + i = joins_.size (); + joins_.push_back (join ()); + table_map_[dt] = i; } + + joins_[i].table = dt; + joins_[i].cond.insert (dcond.str ()); } return true; } private: + bool query_; string table_; semantics::data_member& id_; + + typedef set conditions; + + struct join + { + string table; + conditions cond; + + void + swap (join& o) + { + table.swap (o.table); + cond.swap (o.cond); + } + }; + + typedef vector joins; + typedef map table_map; + + joins joins_; + table_map table_map_; }; const char* integer_buffer_types[] = @@ -1212,10 +1338,10 @@ namespace mysql case ck_map: case ck_multimap: { - if (comp_value (*kt)) + if (semantics::class_* ckt = comp_value (*kt)) { object_columns t (*this, table, false, false); - t.traverse_composite (m, *kt, "key", "key"); + t.traverse_composite (m, *ckt, "key", "key"); } else { @@ -1231,10 +1357,10 @@ namespace mysql } } - if (comp_value (vt)) + if (semantics::class_* cvt = comp_value (vt)) { object_columns t (*this, table, false, false); - t.traverse_composite (m, vt, "value", "value"); + t.traverse_composite (m, *cvt, "value", "value"); } else { @@ -1281,10 +1407,10 @@ namespace mysql case ck_map: case ck_multimap: { - if (comp_value (*kt)) + if (semantics::class_* ckt = comp_value (*kt)) { object_columns t (*this, table, false, false); - t.traverse_composite (m, *kt, "key", "key"); + t.traverse_composite (m, *ckt, "key", "key"); } else { @@ -1300,10 +1426,10 @@ namespace mysql } } - if (comp_value (vt)) + if (semantics::class_* cvt = comp_value (vt)) { object_columns t (*this, table, false, false); - t.traverse_composite (m, vt, "value", "value"); + t.traverse_composite (m, *cvt, "value", "value"); } else { @@ -2109,12 +2235,12 @@ namespace mysql } virtual void - composite (semantics::data_member& m, semantics::type& t) + composite (semantics::data_member& m, semantics::class_& c) { string old (obj_prefix_); obj_prefix_ += m.name (); obj_prefix_ += '.'; - object_members_base::composite (m, t); + object_members_base::composite (m, c); obj_prefix_ = old; } @@ -2305,8 +2431,9 @@ namespace mysql << "\" FROM `" << table << "`\"" << endl; { - object_joins t (*this, c); + object_joins t (*this, c, false); t.traverse (c); + t.write (); } os << "\" WHERE `" << table << "`.`" << column_name (id) << "` = ?\";" @@ -2337,21 +2464,24 @@ namespace mysql // if (options.generate_query ()) { + object_joins oj (*this, c, true); + oj.traverse (c); + + // We only need DISTINCT if there are joins (object pointers) + // and can optimize it out otherwise. + // os << "const char* const " << traits << "::query_clause =" << endl - << "\"SELECT \"" << endl; + << "\"SELECT " << (oj.count () ? "DISTINCT " : "") << "\"" << endl; { - object_columns t (*this, table, true); - t.traverse (c); + object_columns oc (*this, table, true); + oc.traverse (c); } os << "\"" << endl << "\" FROM `" << table << "`\"" << endl; - { - object_joins t (*this, c); - t.traverse (c); - } + oj.write (); os << "\" \";" << endl; -- cgit v1.1