aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2010-11-25 11:19:03 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2010-11-25 11:19:03 +0200
commit1f3fcd13efb06a49bc7363fb58dd1f8d41a069ee (patch)
tree1aa8fa2e453bc0984750faabab7c90b6a3d41fa1
parente7892161b62d0f7f2ccf984d163a91025a3ddf78 (diff)
Add support for object pointers in query
-rw-r--r--odb/common.cxx26
-rw-r--r--odb/common.hxx8
-rw-r--r--odb/mysql/common.cxx97
-rw-r--r--odb/mysql/common.hxx6
-rw-r--r--odb/mysql/schema.cxx8
-rw-r--r--odb/mysql/source.cxx192
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 <odb/gcc.hxx>
+#include <map>
+#include <set>
+#include <vector>
#include <sstream>
#include <odb/mysql/common.hxx>
@@ -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<string> conditions;
+
+ struct join
+ {
+ string table;
+ conditions cond;
+
+ void
+ swap (join& o)
+ {
+ table.swap (o.table);
+ cond.swap (o.cond);
+ }
+ };
+
+ typedef vector<join> joins;
+ typedef map<string, size_t> 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;