summaryrefslogtreecommitdiff
path: root/odb/relational
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2011-03-10 08:44:28 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2011-03-21 15:39:59 +0200
commit7ae497743c7b042904fe1f6b4153ab3f4763ff2b (patch)
tree08ba3f742f20d16d893856ccceb070094bd69225 /odb/relational
parent2436f20262a41bd1cafa5107ab6d6799c03e0964 (diff)
Split MySQL code generator into common and db-specific parts
The common part (in relational/) still has some MySQL-specific parts. Also, add the notion of the current context which is used to avoid explicitly passing the context object to every generator's c-tor.
Diffstat (limited to 'odb/relational')
-rw-r--r--odb/relational/common.cxx178
-rw-r--r--odb/relational/common.hxx303
-rw-r--r--odb/relational/context.cxx54
-rw-r--r--odb/relational/context.hxx81
-rw-r--r--odb/relational/context.ixx31
-rw-r--r--odb/relational/generate.hxx36
-rw-r--r--odb/relational/header.cxx54
-rw-r--r--odb/relational/header.hxx886
-rw-r--r--odb/relational/inline.cxx42
-rw-r--r--odb/relational/inline.hxx107
-rw-r--r--odb/relational/mysql/common.cxx448
-rw-r--r--odb/relational/mysql/common.hxx226
-rw-r--r--odb/relational/mysql/context.cxx642
-rw-r--r--odb/relational/mysql/context.hxx127
-rw-r--r--odb/relational/mysql/header.cxx166
-rw-r--r--odb/relational/mysql/schema.cxx53
-rw-r--r--odb/relational/mysql/source.cxx899
-rw-r--r--odb/relational/schema.cxx101
-rw-r--r--odb/relational/schema.hxx379
-rw-r--r--odb/relational/source.cxx79
-rw-r--r--odb/relational/source.hxx2424
21 files changed, 7316 insertions, 0 deletions
diff --git a/odb/relational/common.cxx b/odb/relational/common.cxx
new file mode 100644
index 0000000..0bcc676
--- /dev/null
+++ b/odb/relational/common.cxx
@@ -0,0 +1,178 @@
+// file : odb/relational/common.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <string>
+#include <cstddef> // std::size_
+#include <cstdlib> // abort
+#include <sstream>
+#include <cxxabi.h> // abi::__cxa_demangle
+
+#include <odb/relational/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ // query_columns
+ //
+
+ query_columns::
+ query_columns ()
+ : ptr_ (true), decl_ (true)
+ {
+ }
+
+ query_columns::
+ query_columns (semantics::class_& cl) //@@ context::object
+ : ptr_ (true), decl_ (false)
+ {
+ scope_ = "access::object_traits< " + cl.fq_name () + " >::query_type";
+ table_ = table_name (cl);
+ }
+
+ void query_columns::
+ composite (semantics::data_member& m, semantics::class_& c)
+ {
+ string name (public_name (m));
+
+ if (decl_)
+ {
+ os << "// " << name << endl
+ << "//" << endl
+ << "struct " << name
+ << "{";
+
+ object_columns_base::composite (m, c);
+
+ os << "};";
+ }
+ else
+ {
+ string old_scope (scope_);
+ scope_ += "::" + name;
+
+ object_columns_base::composite (m, c);
+
+ scope_ = old_scope;
+ }
+ }
+
+ bool query_columns::
+ column (semantics::data_member& m, string const& col_name, bool)
+ {
+ string name (public_name (m));
+
+ if (semantics::class_* c = object_pointer (m.type ()))
+ {
+ // 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 im_type (image_type (m));
+ string db_type (database_type (m));
+
+ string type (
+ "mysql::value_traits< "
+ + m.type ().fq_name (m.belongs ().hint ()) + ", "
+ + im_type + ", "
+ + 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;
+ }
+
+ //
+ // Dynamic traversal support.
+ //
+
+ struct demangled_name
+ {
+ demangled_name (): s (0), n (0) {}
+ ~demangled_name () {free (s);}
+ char* s;
+ size_t n;
+ };
+
+ static demangled_name name_;
+
+ database entry_base::
+ db (type_info const& ti)
+ {
+ char*& s (name_.s);
+
+ int r;
+ s = abi::__cxa_demangle (ti.name (), s, &name_.n, &r);
+
+ if (r != 0)
+ abort (); // We are in static initialization, so this is fatal.
+
+ //
+ //
+ string str (s + 12); // 12 for "relational::"
+ istringstream is (string (str, str.find (':')));
+
+ database d;
+ if (!is >> d)
+ abort ();
+
+ return d;
+ }
+}
diff --git a/odb/relational/common.hxx b/odb/relational/common.hxx
new file mode 100644
index 0000000..513d9d6
--- /dev/null
+++ b/odb/relational/common.hxx
@@ -0,0 +1,303 @@
+// file : odb/relational/common.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_COMMON_HXX
+#define ODB_RELATIONAL_COMMON_HXX
+
+#include <map>
+#include <cstddef> // std::size_t
+#include <cassert>
+#include <typeinfo>
+
+#include <odb/common.hxx>
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ struct member_base: traversal::data_member, virtual context
+ {
+ typedef member_base base;
+
+ member_base (semantics::type* type,
+ string const& fq_type,
+ string const& key_prefix)
+ : type_override_ (type),
+ fq_type_override_ (fq_type),
+ key_prefix_ (key_prefix)
+ {
+ }
+
+ member_base (string const& var,
+ semantics::type* type,
+ string const& fq_type,
+ string const& key_prefix)
+ : var_override_ (var),
+ type_override_ (type),
+ fq_type_override_ (fq_type),
+ key_prefix_ (key_prefix)
+ {
+ }
+
+ protected:
+ // For virtual inheritance only. Should not be actually called.
+ //
+ member_base (); // {assert (false);}
+
+ protected:
+ string var_override_;
+ semantics::type* type_override_;
+ string fq_type_override_;
+ string key_prefix_;
+ };
+
+ //
+ //
+ struct query_columns: object_columns_base, virtual context
+ {
+ typedef query_columns base;
+
+ query_columns ();
+ query_columns (semantics::class_&);
+
+ virtual string
+ image_type (semantics::data_member&)
+ {
+ assert (false);
+ }
+
+ virtual string
+ database_type (semantics::data_member&)
+ {
+ assert (false);
+ }
+
+ virtual void
+ composite (semantics::data_member&, semantics::class_&);
+
+ virtual bool
+ column (semantics::data_member&, string const&, bool);
+
+
+ protected:
+ bool ptr_;
+ bool decl_;
+
+ string scope_;
+ string table_;
+ };
+
+ //
+ // Dynamic traversal support.
+ //
+
+ template <typename B>
+ struct factory
+ {
+ static B*
+ create (B const& prototype)
+ {
+ database db (context::current ().options.database ());
+
+ if (map_ != 0)
+ {
+ typename map::const_iterator i (map_->find (db));
+
+ if (i != map_->end ())
+ return i->second (prototype);
+ }
+
+ return new B (prototype);
+ }
+
+ private:
+ template <typename>
+ friend struct entry;
+
+ static void
+ init ()
+ {
+ if (factory<B>::count_++ == 0)
+ factory<B>::map_ = new typename factory<B>::map;
+ }
+
+ static void
+ term ()
+ {
+ if (--factory<B>::count_ == 0)
+ delete factory<B>::map_;
+ }
+
+ typedef B* (*create_func) (B const&);
+ typedef std::map<database, create_func> map;
+ static map* map_;
+ static std::size_t count_;
+ };
+
+ template <typename B>
+ typename factory<B>::map* factory<B>::map_;
+
+ template <typename B>
+ std::size_t factory<B>::count_;
+
+ struct entry_base
+ {
+ static database
+ db (std::type_info const&);
+ };
+
+ template <typename D>
+ struct entry: entry_base
+ {
+ typedef typename D::base base;
+
+ entry ()
+ {
+ factory<base>::init ();
+ (*factory<base>::map_)[db (typeid (D))] = &create;
+ }
+
+ ~entry ()
+ {
+ factory<base>::term ();
+ }
+
+ static base*
+ create (base const& prototype)
+ {
+ return new D (prototype);
+ }
+ };
+
+ template <typename B>
+ struct instance
+ {
+ typedef relational::factory<B> factory;
+
+ ~instance ()
+ {
+ delete x_;
+ }
+
+ instance ()
+ {
+ B prototype;
+ x_ = factory::create (prototype);
+ }
+
+ template <typename A1>
+ instance (A1& a1)
+ {
+ B prototype (a1);
+ x_ = factory::create (prototype);
+ }
+
+ template <typename A1>
+ instance (A1 const& a1)
+ {
+ B prototype (a1);
+ x_ = factory::create (prototype);
+ }
+
+ template <typename A1, typename A2>
+ instance (A1& a1, A2& a2)
+ {
+ B prototype (a1, a2);
+ x_ = factory::create (prototype);
+ }
+
+ template <typename A1, typename A2>
+ instance (A1 const& a1, A2 const& a2)
+ {
+ B prototype (a1, a2);
+ x_ = factory::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3>
+ instance (A1& a1, A2& a2, A3& a3)
+ {
+ B prototype (a1, a2, a3);
+ x_ = factory::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3>
+ instance (A1 const& a1, A2 const& a2, A3 const& a3)
+ {
+ B prototype (a1, a2, a3);
+ x_ = factory::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4>
+ instance (A1& a1, A2& a2, A3& a3, A4& a4)
+ {
+ B prototype (a1, a2, a3, a4);
+ x_ = factory::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4>
+ instance (A1 const& a1, A2 const& a2, A3 const& a3, A4 const& a4)
+ {
+ B prototype (a1, a2, a3, a4);
+ x_ = factory::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5>
+ instance (A1& a1, A2& a2, A3& a3, A4& a4, A5& a5)
+ {
+ B prototype (a1, a2, a3, a4, a5);
+ x_ = factory::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5>
+ instance (A1 const& a1, A2 const& a2, A3 const& a3, A4 const& a4,
+ A5 const& a5)
+ {
+ B prototype (a1, a2, a3, a4, a5);
+ x_ = factory::create (prototype);
+ }
+
+ instance (instance const& i)
+ {
+ // This is tricky: use the other instance as a prototype.
+ //
+ x_ = factory::create (*i.x_);
+ }
+
+ B*
+ operator-> () const
+ {
+ return x_;
+ }
+
+ B&
+ operator* () const
+ {
+ return *x_;
+ }
+
+ private:
+ instance& operator= (instance const&);
+
+ private:
+ B* x_;
+ };
+
+ template <typename T>
+ inline traversal::edge_base&
+ operator>> (instance<T>& n, traversal::edge_base& e)
+ {
+ n->edge_traverser (e);
+ return e;
+ }
+
+ template <typename T>
+ inline traversal::node_base&
+ operator>> (traversal::edge_base& e, instance<T>& n)
+ {
+ e.node_traverser (*n);
+ return *n;
+ }
+}
+
+#endif // ODB_RELATIONAL_COMMON_HXX
diff --git a/odb/relational/context.cxx b/odb/relational/context.cxx
new file mode 100644
index 0000000..e7a4f21
--- /dev/null
+++ b/odb/relational/context.cxx
@@ -0,0 +1,54 @@
+// file : odb/relational/context.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ context::
+ context ()
+ : data_ (current ().data_)
+ {
+ }
+
+ context::
+ context (data* d)
+ : data_ (d)
+ {
+ }
+
+ bool context::
+ grow_impl (semantics::class_&)
+ {
+ return false;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member&)
+ {
+ return false;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member&,
+ semantics::type&,
+ string const&)
+ {
+ return false;
+ }
+
+ string context::
+ quote_id_impl (string const& id) const
+ {
+ string r;
+ r.reserve (id.size ());
+ r += '"';
+ r += id;
+ r += '"';
+ return r;
+ }
+}
diff --git a/odb/relational/context.hxx b/odb/relational/context.hxx
new file mode 100644
index 0000000..fbd6059
--- /dev/null
+++ b/odb/relational/context.hxx
@@ -0,0 +1,81 @@
+// file : odb/relational/context.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_CONTEXT_HXX
+#define ODB_RELATIONAL_CONTEXT_HXX
+
+#include <odb/context.hxx>
+
+namespace relational
+{
+ class context: public virtual ::context
+ {
+ public:
+ // Return true if an object or value type has members for which
+ // the image can grow.
+ //
+ bool
+ grow (semantics::class_&);
+
+ // The same for a member's value type.
+ //
+ bool
+ grow (semantics::data_member&);
+
+ bool
+ grow (semantics::data_member&, semantics::type&, string const& key_prefix);
+
+ public:
+ // Quote SQL identifier.
+ //
+ string
+ quote_id (string const&) const;
+
+ public:
+ context ();
+
+ static context&
+ current ()
+ {
+ return dynamic_cast<context&> (root_context::current ());
+ }
+
+ protected:
+ // The default implementation returns false.
+ //
+ virtual bool
+ grow_impl (semantics::class_&);
+
+ virtual bool
+ grow_impl (semantics::data_member&);
+
+ virtual bool
+ grow_impl (semantics::data_member&,
+ semantics::type&,
+ string const&);
+
+ // The default implementation uses the ISO quoting ("").
+ //
+ virtual string
+ quote_id_impl (string const&) const;
+
+ protected:
+ struct data;
+ typedef context base_context;
+
+ context (data*);
+
+ protected:
+ struct data: root_context::data
+ {
+ data (std::ostream& os): root_context::data (os) {}
+ };
+ data* data_;
+ };
+}
+
+#include <odb/relational/context.ixx>
+
+#endif // ODB_RELATIONAL_CONTEXT_HXX
diff --git a/odb/relational/context.ixx b/odb/relational/context.ixx
new file mode 100644
index 0000000..da24d39
--- /dev/null
+++ b/odb/relational/context.ixx
@@ -0,0 +1,31 @@
+// file : odb/relational/context.ixx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+namespace relational
+{
+ inline bool context::
+ grow (semantics::class_& c)
+ {
+ return current ().grow_impl (c);
+ }
+
+ inline bool context::
+ grow (semantics::data_member& m)
+ {
+ return current ().grow_impl (m);
+ }
+
+ inline bool context::
+ grow (semantics::data_member& m, semantics::type& t, string const& kp)
+ {
+ return current ().grow_impl (m, t, kp);
+ }
+
+ inline context::string context::
+ quote_id (string const& id) const
+ {
+ return current ().quote_id_impl (id);
+ }
+}
diff --git a/odb/relational/generate.hxx b/odb/relational/generate.hxx
new file mode 100644
index 0000000..c7c7837
--- /dev/null
+++ b/odb/relational/generate.hxx
@@ -0,0 +1,36 @@
+// file : odb/relational/generate.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_GENERATE_HXX
+#define ODB_RELATIONAL_GENERATE_HXX
+
+namespace relational
+{
+ namespace header
+ {
+ void
+ generate ();
+ }
+
+ namespace inline_
+ {
+ void
+ generate ();
+ }
+
+ namespace source
+ {
+ void
+ generate ();
+ }
+
+ namespace schema
+ {
+ void
+ generate ();
+ }
+}
+
+#endif // ODB_RELATIONAL_GENERATE_HXX
diff --git a/odb/relational/header.cxx b/odb/relational/header.cxx
new file mode 100644
index 0000000..f8529f4
--- /dev/null
+++ b/odb/relational/header.cxx
@@ -0,0 +1,54 @@
+// file : odb/relational/header.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/header.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace header
+ {
+ void
+ generate ()
+ {
+ context ctx;
+ ostream& os (ctx.os);
+ options const& ops (ctx.options);
+
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ traversal::namespace_ ns;
+ instance<class_> c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+
+ traversal::defines ns_defines;
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+
+ os << "#include <odb/mysql/version.hxx>" << endl
+ << "#include <odb/mysql/forward.hxx>" << endl
+ << "#include <odb/mysql/mysql-types.hxx>" << endl;
+
+ if (ops.generate_query ())
+ os << "#include <odb/mysql/query.hxx>" << endl;
+
+ os << endl
+ << "#include <odb/details/buffer.hxx>" << endl
+ << endl;
+
+ os << "namespace odb"
+ << "{";
+
+ unit.dispatch (ctx.unit);
+
+ os << "}";
+ }
+ }
+}
diff --git a/odb/relational/header.hxx b/odb/relational/header.hxx
new file mode 100644
index 0000000..12a587d
--- /dev/null
+++ b/odb/relational/header.hxx
@@ -0,0 +1,886 @@
+// file : odb/relational/header.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_HEADER_HXX
+#define ODB_RELATIONAL_HEADER_HXX
+
+#include <odb/gcc.hxx> //@@ ??
+
+#include <odb/relational/context.hxx>
+#include <odb/relational/common.hxx>
+
+namespace relational
+{
+ namespace header
+ {
+ struct image_member: virtual member_base
+ {
+ typedef image_member base;
+
+ image_member (string const& var = string ())
+ : member_base (var, 0, string (), string ())
+ {
+ }
+
+ image_member (string const& var,
+ semantics::type& t,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base (var, &t, fq_type, key_prefix)
+ {
+ }
+ };
+
+ struct image_base: traversal::class_, virtual context
+ {
+ typedef image_base base;
+
+ image_base (): first_ (true) {}
+
+ virtual void
+ traverse (type& c)
+ {
+ // Ignore transient bases.
+ //
+ if (!(c.count ("object") || comp_value (c)))
+ return;
+
+ if (first_)
+ {
+ os << ": ";
+ first_ = false;
+ }
+ else
+ {
+ os << "," << endl
+ << " ";
+ }
+
+ os << "composite_value_traits< " << c.fq_name () << " >::image_type";
+ }
+
+ private:
+ bool first_;
+ };
+
+ struct image_type: traversal::class_, virtual context
+ {
+ typedef image_type base;
+
+ image_type ()
+ {
+ *this >> names_member_ >> member_;
+ }
+
+ image_type (image_type const&)
+ : root_context (), context () //@@ -Wextra
+ {
+ *this >> names_member_ >> member_;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ os << "struct image_type";
+
+ {
+ instance<image_base> b;
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+
+ os << "{";
+
+ names (c);
+
+ if (!comp_value (c))
+ os << "std::size_t version;";
+
+ os << "};";
+ }
+
+ private:
+ instance<image_member> member_;
+ traversal::names names_member_;
+ };
+
+ // Member-specific traits types for container members.
+ //
+ struct container_traits: object_members_base, virtual context
+ {
+ typedef container_traits base;
+
+ container_traits (semantics::class_& obj) //@@ context::object
+ : object_members_base (true, false)
+ {
+ scope_ = "object_traits< " + obj.fq_name () + " >";
+ }
+
+ virtual void
+ container (semantics::data_member& m)
+ {
+ using semantics::type;
+ using semantics::class_;
+
+ type& t (m.type ());
+ container_kind_type ck (container_kind (t));
+
+ type& vt (container_vt (t));
+ type* it (0);
+ type* kt (0);
+
+ bool ordered (false);
+ bool inverse (context::inverse (m, "value"));
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (!unordered (m))
+ {
+ it = &container_it (t);
+ ordered = true;
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ kt = &container_kt (t);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ string name (prefix_ + public_name (m) + "_traits");
+
+ // Figure out column counts.
+ //
+ size_t data_columns (1), cond_columns (1); // One for object id.
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ // Add one for the index.
+ //
+ if (ordered)
+ {
+ data_columns++;
+ cond_columns++;
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ // Add some for the key.
+ //
+ size_t n;
+
+ if (class_* kc = comp_value (*kt))
+ n = in_column_count (*kc);
+ else
+ n = 1;
+
+ data_columns += n;
+ cond_columns += n;
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ // Value is also a key.
+ //
+ if (class_* vc = comp_value (vt))
+ cond_columns += in_column_count (*vc);
+ else
+ cond_columns++;
+
+ break;
+ }
+ }
+
+ if (class_* vc = comp_value (vt))
+ data_columns += in_column_count (*vc);
+ else
+ data_columns++;
+
+ // Store column counts for the source generator.
+ //
+ m.set ("cond-column-count", cond_columns);
+ m.set ("data-column-count", data_columns);
+
+ os << "// " << m.name () << endl
+ << "//" << endl
+ << "struct " << name
+ << "{";
+
+ // container_type
+ // container_traits
+ // index_type
+ // key_type
+ // value_type
+ //
+
+ os << "typedef " << t.fq_name (m.belongs ().hint ()) <<
+ " container_type;";
+ os << "typedef odb::access::container_traits< container_type > " <<
+ "container_traits;";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "typedef container_traits::index_type index_type;";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "typedef container_traits::key_type key_type;";
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ os << "typedef container_traits::value_type value_type;"
+ << endl;
+
+ // functions_type
+ //
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "typedef ordered_functions<index_type, value_type> " <<
+ "functions_type;";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "typedef map_functions<key_type, value_type> " <<
+ "functions_type;";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "typedef set_functions<value_type> functions_type;";
+ break;
+ }
+ }
+
+ os << "typedef mysql::container_statements< " << name <<
+ " > statements_type;"
+ << endl;
+
+ // column_count
+ //
+ os << "static const std::size_t cond_column_count = " <<
+ cond_columns << "UL;"
+ << "static const std::size_t data_column_count = " <<
+ data_columns << "UL;"
+ << endl;
+
+ // id_image_type
+ //
+ os << "typedef " << scope_ << "::id_image_type id_image_type;"
+ << endl;
+
+ // cond_image_type (object id is taken from the object image)
+ //
+ os << "struct cond_image_type"
+ << "{";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<image_member> im ("index_", *it, "index_type", "index");
+ im->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<image_member> im ("key_", *kt, "key_type", "key");
+ im->traverse (m);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "// value" << endl
+ << "//" << endl;
+ instance<image_member> im ("value_", vt, "value_type", "value");
+ im->traverse (m);
+ break;
+ }
+ }
+
+ os << "std::size_t version;"
+ << "};";
+
+ // data_image_type (object id is taken from the object image)
+ //
+ os << "struct data_image_type"
+ << "{";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<image_member> im ("index_", *it, "index_type", "index");
+ im->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<image_member> im ("key_", *kt, "key_type", "key");
+ im->traverse (m);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ os << "// value" << endl
+ << "//" << endl;
+ instance<image_member> im ("value_", vt, "value_type", "value");
+ im->traverse (m);
+
+ os << "std::size_t version;"
+ << "};";
+
+ // Statements.
+ //
+ os << "static const char* const insert_one_statement;"
+ << "static const char* const select_all_statement;"
+ << "static const char* const delete_all_statement;"
+ << endl;
+
+ // bind (cond_image)
+ //
+ os << "static void" << endl
+ << "bind (MYSQL_BIND*, id_image_type*, cond_image_type&);"
+ << endl;
+
+ // bind (data_image)
+ //
+ os << "static void" << endl
+ << "bind (MYSQL_BIND*, id_image_type*, data_image_type&);"
+ << endl;
+
+ // grow ()
+ //
+ os << "static void" << endl
+ << "grow (data_image_type&, my_bool*);"
+ << endl;
+
+ // init (data_image)
+ //
+ if (!inverse)
+ {
+ os << "static void" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "init (data_image_type&, index_type, const value_type&);";
+ else
+ os << "init (data_image_type&, const value_type&);";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "init (data_image_type&, const key_type&, const value_type&);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "init (data_image_type&, const value_type&);";
+ break;
+ }
+ }
+
+ os << endl;
+ }
+
+ // init (data)
+ //
+ os << "static void" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "init (index_type&, value_type&, ";
+ else
+ os << "init (value_type&, ";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "init (key_type&, value_type&, ";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "init (value_type&, ";
+ break;
+ }
+ }
+
+ os << "const data_image_type&, database&);"
+ << endl;
+
+ // insert_one
+ //
+ os << "static void" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "insert_one (index_type, const value_type&, void*);";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "insert_one (const key_type&, const value_type&, void*);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "insert_one (const value_type&, void*);";
+ break;
+ }
+ }
+
+ os << endl;
+
+ // load_all
+ //
+ os << "static bool" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "load_all (index_type&, value_type&, void*);";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "load_all (key_type&, value_type&, void*);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "load_all (value_type&, void*);";
+ break;
+ }
+ }
+
+ os << endl;
+
+ // delete_all
+ //
+ os << "static void" << endl
+ << "delete_all (void*);"
+ << endl;
+
+ // persist
+ //
+ if (!inverse)
+ os << "static void" << endl
+ << "persist (const container_type&," << endl
+ << "id_image_type&," << endl
+ << "statements_type&);"
+ << endl;
+
+ // load
+ //
+ os << "static void" << endl
+ << "load (container_type&," << endl
+ << "id_image_type&," << endl
+ << "statements_type&);"
+ << endl;
+
+ // update
+ //
+ if (!inverse)
+ os << "static void" << endl
+ << "update (const container_type&," << endl
+ << "id_image_type&," << endl
+ << "statements_type&);"
+ << endl;
+
+ // erase
+ //
+ if (!inverse)
+ os << "static void" << endl
+ << "erase (id_image_type&, statements_type&);"
+ << endl;
+
+ os << "};";
+ }
+
+ private:
+ string scope_;
+ };
+
+ //
+ //
+ struct class_: traversal::class_, virtual context
+ {
+ typedef class_ base;
+
+ class_ (): id_image_member_ ("id_") {}
+
+ class_ (class_ const&)
+ : root_context (), //@@ -Wextra
+ context (),
+ id_image_member_ ("id_")
+ {
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ if (c.file () != unit.file ())
+ return;
+
+ if (c.count ("object"))
+ traverse_object (c);
+ else if (comp_value (c))
+ traverse_value (c);
+ }
+
+ virtual void
+ traverse_object (type& c)
+ {
+ string const& type (c.fq_name ());
+ bool def_ctor (TYPE_HAS_DEFAULT_CONSTRUCTOR (c.tree_node ()));
+
+ semantics::data_member& id (id_member (c));
+ bool auto_id (id.count ("auto"));
+
+ os << "// " << c.name () << endl
+ << "//" << endl;
+
+ os << "template <>" << endl
+ << "class access::object_traits< " << type << " >"
+ << "{"
+ << "public:" << endl;
+
+ // object_type & pointer_type
+ //
+ os << "typedef " << type << " object_type;"
+ << "typedef " << c.get<string> ("object-pointer") << " pointer_type;";
+
+ // id_type
+ //
+ os << "typedef " << id.type ().fq_name (id.belongs ().hint ()) <<
+ " id_type;"
+ << endl;
+
+ // image_type
+ //
+ image_type_->traverse (c);
+
+ // id_image_type
+ //
+ os << "struct id_image_type"
+ << "{";
+
+ id_image_member_->traverse (id);
+
+ os << "std::size_t version;"
+ << "};";
+
+ // query_type & query_base_type
+ //
+ if (options.generate_query ())
+ {
+ // query_base_type
+ //
+ os << "typedef mysql::query query_base_type;"
+ << endl;
+
+ // query_type
+ //
+ os << "struct query_type: query_base_type"
+ << "{";
+
+ {
+ instance<query_columns> t;
+ t->traverse (c);
+ }
+
+ os << "query_type ();"
+ << "query_type (const std::string&);"
+ << "query_type (const query_base_type&);"
+ << "};";
+ }
+
+ // column_count
+ //
+ os << "static const std::size_t in_column_count = " <<
+ in_column_count (c) << "UL;"
+ << "static const std::size_t out_column_count = " <<
+ out_column_count (c) << "UL;"
+ << endl;
+
+ // Statements.
+ //
+ os << "static const char* const persist_statement;"
+ << "static const char* const find_statement;"
+ << "static const char* const update_statement;"
+ << "static const char* const erase_statement;";
+
+ if (options.generate_query ())
+ os << "static const char* const query_clause;";
+
+ os << endl;
+
+ //
+ // Containers.
+ //
+
+ // Traits types.
+ //
+ {
+ instance<container_traits> t (c);
+ t->traverse (c);
+ }
+
+ // Statement cache (forward declaration).
+ //
+ os << "struct container_statement_cache_type;"
+ << endl;
+
+ //
+ // Functions.
+ //
+
+ // id ()
+ //
+ os << "static id_type" << endl
+ << "id (const object_type&);"
+ << endl;
+
+ if (options.generate_query ())
+ os << "static id_type" << endl
+ << "id (const image_type&);"
+ << endl;
+
+ // grow ()
+ //
+ os << "static void" << endl
+ << "grow (image_type&, my_bool*);"
+ << endl;
+
+ // bind (image_type)
+ //
+ os << "static void" << endl
+ << "bind (MYSQL_BIND*, image_type&, bool);"
+ << endl;
+
+ // bind (id_image_type)
+ //
+ os << "static void" << endl
+ << "bind (MYSQL_BIND*, id_image_type&);"
+ << endl;
+
+ // init (image, object)
+ //
+ os << "static void" << endl
+ << "init (image_type&, const object_type&);"
+ << endl;
+
+ // init (object, image)
+ //
+ os << "static void" << endl
+ << "init (object_type&, const image_type&, database&);"
+ << endl;
+
+ // init (id_image, id)
+ //
+ os << "static void" << endl
+ << "init (id_image_type&, const id_type&);"
+ << endl;
+
+ // persist ()
+ //
+ os << "static void" << endl
+ << "persist (database&, " << (auto_id ? "" : "const ") <<
+ "object_type&);"
+ << endl;
+
+ // update ()
+ //
+ os << "static void" << endl
+ << "update (database&, const object_type&);"
+ << endl;
+
+ // erase ()
+ //
+ os << "static void" << endl
+ << "erase (database&, const id_type&);"
+ << endl;
+
+ // find ()
+ //
+ if (def_ctor)
+ os << "static pointer_type" << endl
+ << "find (database&, const id_type&);"
+ << endl;
+
+ os << "static bool" << endl
+ << "find (database&, const id_type&, object_type&);"
+ << endl;
+
+ // query ()
+ //
+ if (options.generate_query ())
+ os << "template<typename T>" << endl
+ << "static result<T>" << endl
+ << "query (database&, const query_type&);"
+ << endl;
+
+ // create_schema ()
+ //
+ if (embedded_schema)
+ {
+ os << "static void" << endl
+ << "create_schema (database&);"
+ << endl;
+ }
+
+ // Implementation details.
+ //
+ os << "public:" << endl;
+
+ // Load the object image.
+ //
+ os << "static bool" << endl
+ << "find_ (mysql::object_statements< object_type >&, const id_type&);"
+ << endl;
+
+ // Load the rest of the object (containers, etc). Expects the id
+ // image in the object statements to be initialized to the object
+ // id.
+ //
+ os << "static void" << endl
+ << "load_ (mysql::object_statements< object_type >&, object_type&);"
+ << endl;
+
+ if (options.generate_query ())
+ os << "static void" << endl
+ << "query_ (database&," << endl
+ << "const query_type&," << endl
+ << "mysql::object_statements< object_type >&," << endl
+ << "details::shared_ptr< mysql::select_statement >&);"
+ << endl;
+
+ os << "};";
+ }
+
+ virtual void
+ traverse_value (type& c)
+ {
+ string const& type (c.fq_name ());
+
+ os << "// " << c.name () << endl
+ << "//" << endl;
+
+ os << "template <>" << endl
+ << "class access::composite_value_traits< " << type << " >"
+ << "{"
+ << "public:" << endl;
+
+ // object_type
+ //
+ os << "typedef " << type << " value_type;"
+ << endl;
+
+ // image_type
+ //
+ image_type_->traverse (c);
+
+ // grow ()
+ //
+ os << "static bool" << endl
+ << "grow (image_type&, my_bool*);"
+ << endl;
+
+ // bind (image_type)
+ //
+ os << "static void" << endl
+ << "bind (MYSQL_BIND*, image_type&);"
+ << endl;
+
+ // init (image, object)
+ //
+ os << "static bool" << endl
+ << "init (image_type&, const value_type&);"
+ << endl;
+
+ // init (object, image)
+ //
+ os << "static void" << endl
+ << "init (value_type&, const image_type&, database&);"
+ << endl;
+
+ os << "};";
+ }
+
+ private:
+ instance<image_type> image_type_;
+ instance<image_member> id_image_member_;
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_HEADER_HXX
diff --git a/odb/relational/inline.cxx b/odb/relational/inline.cxx
new file mode 100644
index 0000000..e5371ab
--- /dev/null
+++ b/odb/relational/inline.cxx
@@ -0,0 +1,42 @@
+// file : odb/relational/inline.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/inline.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace inline_
+ {
+ void
+ generate ()
+ {
+ context ctx;
+ ostream& os (ctx.os);
+
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ traversal::namespace_ ns;
+ class_ c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+
+ traversal::defines ns_defines;
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+
+ os << "namespace odb"
+ << "{";
+
+ unit.dispatch (ctx.unit);
+
+ os << "}";
+ }
+ }
+}
diff --git a/odb/relational/inline.hxx b/odb/relational/inline.hxx
new file mode 100644
index 0000000..ef0e32e
--- /dev/null
+++ b/odb/relational/inline.hxx
@@ -0,0 +1,107 @@
+// file : odb/relational/inline.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_INLINE_HXX
+#define ODB_RELATIONAL_INLINE_HXX
+
+#include <odb/relational/context.hxx>
+#include <odb/relational/common.hxx>
+
+namespace relational
+{
+ namespace inline_
+ {
+ struct class_: traversal::class_, virtual context
+ {
+ typedef class_ base;
+
+ virtual void
+ traverse (type& c)
+ {
+ if (c.file () != unit.file ())
+ return;
+
+ if (c.count ("object"))
+ traverse_object (c);
+ else if (comp_value (c))
+ traverse_value (c);
+ }
+
+ virtual void
+ traverse_object (type& c)
+ {
+ string const& type (c.fq_name ());
+ string traits ("access::object_traits< " + type + " >");
+ semantics::data_member& id (id_member (c));
+
+ os << "// " << c.name () << endl
+ << "//" << endl
+ << endl;
+
+ // query_type
+ //
+ if (options.generate_query ())
+ {
+ os << "inline" << endl
+ << traits << "::query_type::" << endl
+ << "query_type ()"
+ << "{"
+ << "}";
+
+ os << "inline" << endl
+ << traits << "::query_type::" << endl
+ << "query_type (const std::string& q)" << endl
+ << " : query_base_type (q)"
+ << "{"
+ << "}";
+
+ os << "inline" << endl
+ << traits << "::query_type::" << endl
+ << "query_type (const query_base_type& q)" << endl
+ << " : query_base_type (q)"
+ << "{"
+ << "}";
+ }
+
+ // id ()
+ //
+ os << "inline" << endl
+ << traits << "::id_type" << endl
+ << traits << "::" << endl
+ << "id (const object_type& obj)"
+ << "{"
+ << "return obj." << id.name () << ";" << endl
+ << "}";
+
+ // load_()
+ //
+ if (!has_a (c, test_container))
+ {
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "load_ (mysql::object_statements< object_type >&, object_type&)"
+ << "{"
+ << "}";
+ }
+ }
+
+ virtual void
+ traverse_value (type&)
+ {
+ /*
+ string const& type (c.fq_name ());
+ string traits ("access::composite_value_traits< " + type + " >");
+
+ os << "// " << c.name () << endl
+ << "//" << endl
+ << endl;
+
+ */
+ }
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_INLINE_HXX
diff --git a/odb/relational/mysql/common.cxx b/odb/relational/mysql/common.cxx
new file mode 100644
index 0000000..d45229a
--- /dev/null
+++ b/odb/relational/mysql/common.cxx
@@ -0,0 +1,448 @@
+// file : odb/relational/mysql/common.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/relational/mysql/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mysql
+ {
+ //
+ // member_base
+ //
+
+ void member_base::
+ traverse (semantics::data_member& m)
+ {
+ if (m.count ("transient"))
+ return;
+
+ string var;
+
+ if (!var_override_.empty ())
+ var = var_override_;
+ else
+ {
+ string const& name (m.name ());
+ var = name + (name[name.size () - 1] == '_' ? "" : "_");
+ }
+
+ semantics::type& t (type_override_ != 0 ? *type_override_ : m.type ());
+
+ if (comp_value (t))
+ {
+ member_info mi (m, t, var, fq_type_override_);
+ if (pre (mi))
+ {
+ traverse_composite (mi);
+ post (mi);
+ }
+ }
+ else if (container (t))
+ {
+ member_info mi (m, t, var, fq_type_override_);
+ if (pre (mi))
+ {
+ traverse_container (mi);
+ post (mi);
+ }
+ }
+ else
+ {
+ sql_type const& st (db_type (m, key_prefix_));
+
+ if (semantics::class_* c = object_pointer (t))
+ {
+ member_info mi (m, id_member (*c).type (), var, fq_type_override_);
+ mi.st = &st;
+ if (pre (mi))
+ {
+ traverse_object_pointer (mi);
+ post (mi);
+ }
+ }
+ else
+ {
+ member_info mi (m, t, var, fq_type_override_);
+ mi.st = &st;
+ if (pre (mi))
+ {
+ traverse_simple (mi);
+ post (mi);
+ }
+ }
+ }
+ }
+
+ void member_base::
+ traverse_simple (member_info& mi)
+ {
+ switch (mi.st->type)
+ {
+ // Integral types.
+ //
+ case sql_type::TINYINT:
+ case sql_type::SMALLINT:
+ case sql_type::MEDIUMINT:
+ case sql_type::INT:
+ case sql_type::BIGINT:
+ {
+ traverse_integer (mi);
+ break;
+ }
+
+ // Float types.
+ //
+ case sql_type::FLOAT:
+ case sql_type::DOUBLE:
+ {
+ traverse_float (mi);
+ break;
+ }
+ case sql_type::DECIMAL:
+ {
+ traverse_decimal (mi);
+ break;
+ }
+
+ // Data-time types.
+ //
+ case sql_type::DATE:
+ case sql_type::TIME:
+ case sql_type::DATETIME:
+ case sql_type::TIMESTAMP:
+ case sql_type::YEAR:
+ {
+ traverse_date_time (mi);
+ break;
+ }
+
+ // String and binary types.
+ //
+ case sql_type::CHAR:
+ case sql_type::VARCHAR:
+ case sql_type::TINYTEXT:
+ case sql_type::TEXT:
+ case sql_type::MEDIUMTEXT:
+ case sql_type::LONGTEXT:
+ {
+ // For string types the limit is in characters rather
+ // than in bytes. The fixed-length pre-allocated buffer
+ // optimization can only be used for 1-byte encodings.
+ // To support this we will need the character encoding
+ // in sql_type.
+ //
+ traverse_long_string (mi);
+ break;
+ }
+ case sql_type::BINARY:
+ case sql_type::TINYBLOB:
+ {
+ // BINARY's range is always 255 or less from MySQL 5.0.3.
+ // TINYBLOB can only store up to 255 bytes.
+ //
+ traverse_short_string (mi);
+ break;
+ }
+ case sql_type::VARBINARY:
+ case sql_type::BLOB:
+ case sql_type::MEDIUMBLOB:
+ case sql_type::LONGBLOB:
+ {
+ if (mi.st->range && mi.st->range_value <= 255)
+ traverse_short_string (mi);
+ else
+ traverse_long_string (mi);
+
+ break;
+ }
+
+ // Other types.
+ //
+ case sql_type::BIT:
+ {
+ traverse_bit (mi);
+ break;
+ }
+ case sql_type::ENUM:
+ {
+ traverse_enum (mi);
+ break;
+ }
+ case sql_type::SET:
+ {
+ traverse_set (mi);
+ break;
+ }
+ case sql_type::invalid:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ //
+ // member_image_type
+ //
+
+ namespace
+ {
+ const char* integer_types[] =
+ {
+ "char",
+ "short",
+ "int",
+ "int",
+ "long long"
+ };
+
+ const char* float_types[] =
+ {
+ "float",
+ "double"
+ };
+ }
+
+ member_image_type::
+ member_image_type (semantics::type* type,
+ string const& fq_type,
+ string const& key_prefix)
+ : relational::member_base (type, fq_type, key_prefix)
+ {
+ }
+
+ string member_image_type::
+ image_type (semantics::data_member& m)
+ {
+ type_.clear ();
+ member_base::traverse (m);
+ return type_;
+ }
+
+ void member_image_type::
+ traverse_composite (member_info& mi)
+ {
+ type_ = "composite_value_traits< " + mi.fq_type () + " >::image_type";
+ }
+
+ void member_image_type::
+ traverse_integer (member_info& mi)
+ {
+ if (mi.st->unsign)
+ type_ = "unsigned ";
+ else if (mi.st->type == sql_type::TINYINT)
+ type_ = "signed ";
+
+ type_ += integer_types[mi.st->type - sql_type::TINYINT];
+ }
+
+ void member_image_type::
+ traverse_float (member_info& mi)
+ {
+ type_ = float_types[mi.st->type - sql_type::FLOAT];
+ }
+
+ void member_image_type::
+ traverse_decimal (member_info&)
+ {
+ type_ = "details::buffer";
+ }
+
+ void member_image_type::
+ traverse_date_time (member_info& mi)
+ {
+ if (mi.st->type == sql_type::YEAR)
+ type_ = "short";
+ else
+ type_ = "MYSQL_TIME";
+ }
+
+ void member_image_type::
+ traverse_string (member_info&)
+ {
+ type_ = "details::buffer";
+ }
+
+ void member_image_type::
+ traverse_bit (member_info&)
+ {
+ type_ = "unsigned char*";
+ }
+
+ void member_image_type::
+ traverse_enum (member_info&)
+ {
+ // Represented as string.
+ //
+ type_ = "details::buffer";
+ }
+
+ void member_image_type::
+ traverse_set (member_info&)
+ {
+ // Represented as string.
+ //
+ type_ = "details::buffer";
+ }
+
+ //
+ // member_database_type
+ //
+
+ namespace
+ {
+ const char* integer_database_id[] =
+ {
+ "id_tiny",
+ "id_utiny",
+ "id_short",
+ "id_ushort",
+ "id_long", // INT24
+ "id_ulong", // INT24 UNSIGNED
+ "id_long",
+ "id_ulong",
+ "id_longlong",
+ "id_ulonglong"
+ };
+
+ const char* float_database_id[] =
+ {
+ "id_float",
+ "id_double"
+ };
+
+ const char* date_time_database_id[] =
+ {
+ "id_date",
+ "id_time",
+ "id_datetime",
+ "id_timestamp",
+ "id_year"
+ };
+
+ const char* char_bin_database_id[] =
+ {
+ "id_string", // CHAR
+ "id_blob", // BINARY,
+ "id_string", // VARCHAR
+ "id_blob", // VARBINARY
+ "id_string", // TINYTEXT
+ "id_blob", // TINYBLOB
+ "id_string", // TEXT
+ "id_blob", // BLOB
+ "id_string", // MEDIUMTEXT
+ "id_blob", // MEDIUMBLOB
+ "id_string", // LONGTEXT
+ "id_blob" // LONGBLOB
+ };
+ }
+
+ member_database_type::
+ member_database_type (semantics::type* type,
+ string const& fq_type,
+ string const& key_prefix)
+ : relational::member_base (type, fq_type, key_prefix)
+ {
+ }
+
+ string member_database_type::
+ database_type (type& m)
+ {
+ type_.clear ();
+ member_base::traverse (m);
+ return type_;
+ }
+
+ void member_database_type::
+ traverse_composite (member_info&)
+ {
+ assert (false);
+ }
+
+ void member_database_type::
+ traverse_integer (member_info& mi)
+ {
+ size_t i (
+ (mi.st->type - sql_type::TINYINT) * 2 + (mi.st->unsign ? 1 : 0));
+ type_ = string ("mysql::") + integer_database_id[i];
+ }
+
+ void member_database_type::
+ traverse_float (member_info& mi)
+ {
+ type_ = string ("mysql::") +
+ float_database_id[mi.st->type - sql_type::FLOAT];
+ }
+
+ void member_database_type::
+ traverse_decimal (member_info&)
+ {
+ type_ = "mysql::id_decimal";
+ }
+
+ void member_database_type::
+ traverse_date_time (member_info& mi)
+ {
+ type_ = string ("mysql::") +
+ date_time_database_id[mi.st->type - sql_type::DATE];
+ }
+
+ void member_database_type::
+ traverse_string (member_info& mi)
+ {
+ type_ = string ("mysql::") +
+ char_bin_database_id[mi.st->type - sql_type::CHAR];
+ }
+
+ void member_database_type::
+ traverse_bit (member_info&)
+ {
+ type_ = "mysql::id_bit";
+ }
+
+ void member_database_type::
+ traverse_enum (member_info&)
+ {
+ type_ = "mysql::id_enum";
+ }
+
+ void member_database_type::
+ traverse_set (member_info&)
+ {
+ type_ = "mysql::id_set";
+ }
+
+ //
+ // query_columns
+ //
+
+ struct query_columns: relational::query_columns, context
+ {
+ query_columns (base const& x): base (x) {}
+
+ virtual string
+ image_type (semantics::data_member& m)
+ {
+ return member_image_type_.image_type (m);
+ }
+
+ virtual string
+ database_type (semantics::data_member& m)
+ {
+ return member_database_type_.database_type (m);
+ }
+
+ private:
+ member_image_type member_image_type_;
+ member_database_type member_database_type_;
+ };
+ entry<query_columns> query_columns_;
+ }
+}
diff --git a/odb/relational/mysql/common.hxx b/odb/relational/mysql/common.hxx
new file mode 100644
index 0000000..e58390f
--- /dev/null
+++ b/odb/relational/mysql/common.hxx
@@ -0,0 +1,226 @@
+// file : odb/relational/mysql/common.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_MYSQL_COMMON_HXX
+#define ODB_RELATIONAL_MYSQL_COMMON_HXX
+
+#include <odb/relational/common.hxx>
+#include <odb/relational/mysql/context.hxx>
+
+namespace relational
+{
+ namespace mysql
+ {
+ struct member_base: virtual relational::member_base, context
+ {
+ member_base (base const& x): base (x) {}
+
+ // This c-tor is for the direct use inside the mysql namespace.
+ // If you do use this c-tor, you should also explicitly call
+ // relational::member_base.
+ //
+ member_base () {}
+
+ virtual void
+ traverse (semantics::data_member& m);
+
+ struct member_info
+ {
+ semantics::data_member& m; // Member.
+ semantics::type& t; // Member C++ type (m.type () may != t).
+ sql_type const* st; // Member SQL type (only simple values).
+ string& var; // Member variable name with trailing '_'.
+
+ // C++ type fq-name.
+ //
+ string
+ fq_type () const
+ {
+ // Use the original type from 'm' instead of 't' since the hint
+ // may be invalid for a different type. Plus, if a type is
+ // overriden, then the fq_type must be as well.
+ //
+ return fq_type_.empty ()
+ ? m.type ().fq_name (m.belongs ().hint ())
+ : fq_type_;
+ }
+
+ string const& fq_type_;
+
+ member_info (semantics::data_member& m_,
+ semantics::type& t_,
+ string& var_,
+ string const& fq_type)
+ : m (m_), t (t_), st (0), var (var_), fq_type_ (fq_type)
+ {
+ }
+ };
+
+ // The false return value indicates that no further callbacks
+ // should be called for this member.
+ //
+ virtual bool
+ pre (member_info&)
+ {
+ return true;
+ }
+
+ virtual void
+ post (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_composite (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_container (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_object_pointer (member_info& mi)
+ {
+ traverse_simple (mi);
+ }
+
+ virtual void
+ traverse_simple (member_info&);
+
+ virtual void
+ traverse_integer (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_float (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_decimal (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_date_time (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_string (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_short_string (member_info& mi)
+ {
+ traverse_string (mi);
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ traverse_string (mi);
+ }
+
+ virtual void
+ traverse_bit (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_enum (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_set (member_info&)
+ {
+ }
+ };
+
+ struct member_image_type: member_base
+ {
+ member_image_type (semantics::type* type = 0,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+ string
+ image_type (semantics::data_member&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_integer (member_info&);
+
+ virtual void
+ traverse_float (member_info&);
+
+ virtual void
+ traverse_decimal (member_info&);
+
+ virtual void
+ traverse_date_time (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_bit (member_info&);
+
+ virtual void
+ traverse_enum (member_info&);
+
+ virtual void
+ traverse_set (member_info&);
+
+ private:
+ string type_;
+ };
+
+ struct member_database_type: member_base
+ {
+ member_database_type (semantics::type* type = 0,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+ string
+ database_type (type&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_integer (member_info&);
+
+ virtual void
+ traverse_float (member_info&);
+
+ virtual void
+ traverse_decimal (member_info&);
+
+ virtual void
+ traverse_date_time (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_bit (member_info&);
+
+ virtual void
+ traverse_enum (member_info&);
+
+ virtual void
+ traverse_set (member_info&);
+
+ private:
+ string type_;
+ };
+ }
+}
+#endif // ODB_RELATIONAL_MYSQL_COMMON_HXX
diff --git a/odb/relational/mysql/context.cxx b/odb/relational/mysql/context.cxx
new file mode 100644
index 0000000..014aa7d
--- /dev/null
+++ b/odb/relational/mysql/context.cxx
@@ -0,0 +1,642 @@
+// file : odb/relational/mysql/context.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/sql-token.hxx>
+#include <odb/sql-lexer.hxx>
+
+#include <odb/relational/mysql/context.hxx>
+#include <odb/relational/mysql/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mysql
+ {
+ namespace
+ {
+ struct type_map_entry
+ {
+ const char* const cxx_type;
+ const char* const db_type;
+ const char* const db_id_type;
+ };
+
+ type_map_entry type_map[] =
+ {
+ {"bool", "TINYINT(1)", 0},
+
+ {"char", "TINYINT", 0},
+ {"signed char", "TINYINT", 0},
+ {"unsigned char", "TINYINT UNSIGNED", 0},
+
+ {"short int", "SMALLINT", 0},
+ {"short unsigned int", "SMALLINT UNSIGNED", 0},
+
+ {"int", "INT", 0},
+ {"unsigned int", "INT UNSIGNED", 0},
+
+ {"long int", "BIGINT", 0},
+ {"long unsigned int", "BIGINT UNSIGNED", 0},
+
+ {"long long int", "BIGINT", 0},
+ {"long long unsigned int", "BIGINT UNSIGNED", 0},
+
+ {"float", "FLOAT", 0},
+ {"double", "DOUBLE", 0},
+
+ {"::std::string", "TEXT", "VARCHAR (255)"}
+ };
+ }
+
+ context::
+ context (ostream& os, semantics::unit& u, options_type const& ops)
+ : root_context (os, u, ops, data_ptr (new (shared) data (os))),
+ base_context (static_cast<data*> (root_context::data_.get ())),
+ data_ (static_cast<data*> (base_context::data_))
+ {
+ // Populate the C++ type to DB type map.
+ //
+ for (size_t i (0); i < sizeof (type_map) / sizeof (type_map_entry); ++i)
+ {
+ type_map_entry const& e (type_map[i]);
+
+ type_map_type::value_type v (
+ e.cxx_type,
+ db_type_type (e.db_type, e.db_id_type ? e.db_id_type : e.db_type));
+
+ data_->type_map_.insert (v);
+ }
+ }
+
+ context::
+ context ()
+ : data_ (current ().data_)
+ {
+ }
+
+ string context::
+ quote_id_impl (string const& id) const
+ {
+ string r;
+ r.reserve (id.size ());
+ r += '`';
+ r += id;
+ r += '`';
+ return r;
+ }
+
+ namespace
+ {
+ struct has_grow: traversal::class_
+ {
+ has_grow (bool& r)
+ : r_ (r)
+ {
+ *this >> inherits_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ // Ignore transient bases.
+ //
+ if (!(c.count ("object") || context::comp_value (c)))
+ return;
+
+ if (c.count ("mysql::grow"))
+ r_ = c.get<bool> ("mysql::grow");
+ else
+ {
+ // r_ should be false.
+ //
+ inherits (c);
+
+ if (!r_)
+ names (c);
+
+ c.set ("mysql::grow", r_);
+ }
+ }
+
+ private:
+ bool& r_;
+ traversal::inherits inherits_;
+ };
+
+ struct has_grow_member: member_base
+ {
+ has_grow_member (bool& r,
+ semantics::type* type = 0,
+ string const& key_prefix = string ())
+ : relational::member_base (type, string (), key_prefix),
+ r_ (r)
+ {
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ // By calling grow() instead of recursing, we reset any overrides.
+ //
+ r_ = r_ || context::grow (dynamic_cast<semantics::class_&> (mi.t));
+ }
+
+ virtual void
+ traverse_decimal (member_info&)
+ {
+ r_ = true;
+ }
+
+ virtual void
+ traverse_long_string (member_info&)
+ {
+ r_ = true;
+ }
+
+ virtual void
+ traverse_short_string (member_info&)
+ {
+ r_ = true; // @@ Short string optimization disabled.
+ }
+
+ virtual void
+ traverse_enum (member_info&)
+ {
+ r_ = true;
+ }
+
+ virtual void
+ traverse_set (member_info&)
+ {
+ r_ = true;
+ }
+
+ private:
+ bool& r_;
+ };
+ }
+
+ bool context::
+ grow_impl (semantics::class_& c)
+ {
+ if (c.count ("mysql::grow"))
+ return c.get<bool> ("mysql::grow");
+
+ bool r (false);
+ has_grow ct (r);
+ has_grow_member mt (r);
+ traversal::names names;
+ ct >> names >> mt;
+ ct.traverse (c);
+ return r;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member& m)
+ {
+ bool r (false);
+ has_grow_member mt (r);
+ mt.traverse (m);
+ return r;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member& m, semantics::type& t, string const& kp)
+ {
+ bool r (false);
+ has_grow_member mt (r, &t, kp);
+ mt.traverse (m);
+ return r;
+ }
+
+ //
+ // SQL type parsing.
+ //
+
+ string context::data::
+ column_type_impl (semantics::type& t,
+ string const& type,
+ semantics::context& ctx,
+ column_type_flags f) const
+ {
+ string r (::context::data::column_type_impl (t, type, ctx, f));
+
+ if (!r.empty () && ctx.count ("auto") && (f & ctf_object_id_ref) == 0)
+ r += " AUTO_INCREMENT";
+
+ return r;
+ }
+
+ static sql_type
+ parse_sql_type (semantics::data_member& m, std::string const& sql);
+
+ sql_type const& context::
+ db_type (semantics::data_member& m, string const& kp)
+ {
+ string key (kp.empty () ? string ("db-type") : kp + "-db-type");
+
+ if (!m.count (key))
+ m.set (key, parse_sql_type (m, column_type (m, kp)));
+
+ return m.get<sql_type> (key);
+ }
+
+ static sql_type
+ parse_sql_type (semantics::data_member& m, string const& sql)
+ {
+ try
+ {
+ sql_type r;
+ sql_lexer l (sql);
+
+ // While most type names use single identifier, there are
+ // a couple of exceptions to this rule:
+ //
+ // NATIONAL CHAR|VARCHAR
+ // CHAR BYTE (BINARY)
+ // CHARACTER VARYING (VARCHAR)
+ // LONG VARBINARY (MEDIUMBLOB)
+ // LONG VARCHAR (MEDIUMTEXT)
+ //
+ //
+ enum state
+ {
+ parse_prefix,
+ parse_name,
+ parse_range,
+ parse_sign,
+ parse_done
+ };
+
+ state s (parse_prefix);
+ string prefix;
+
+ for (sql_token t (l.next ());
+ s != parse_done && t.type () != sql_token::t_eos;
+ t = l.next ())
+ {
+ sql_token::token_type tt (t.type ());
+
+ switch (s)
+ {
+ case parse_prefix:
+ {
+ if (tt == sql_token::t_identifier)
+ {
+ string const& id (t.identifier ());
+
+ if (id == "NATIONAL" ||
+ id == "CHAR" ||
+ id == "CHARACTER" ||
+ id == "LONG")
+ {
+ prefix = id;
+ s = parse_name;
+ continue;
+ }
+ }
+
+ // Fall through.
+ //
+ s = parse_name;
+ }
+ case parse_name:
+ {
+ if (tt == sql_token::t_identifier)
+ {
+ bool match (true);
+ string const& id (t.identifier ());
+
+ // Numeric types.
+ //
+ if (id == "BIT")
+ {
+ r.type = sql_type::BIT;
+ }
+ else if (id == "TINYINT" || id == "INT1")
+ {
+ r.type = sql_type::TINYINT;
+ }
+ else if (id == "BOOL" || id == "BOOLEAN")
+ {
+ r.type = sql_type::TINYINT;
+ r.range = true;
+ r.range_value = 1;
+ }
+ else if (id == "SMALLINT" || id == "INT2")
+ {
+ r.type = sql_type::SMALLINT;
+ }
+ else if (id == "MEDIUMINT" ||
+ id == "INT3" ||
+ id == "MIDDLEINT")
+ {
+ r.type = sql_type::MEDIUMINT;
+ }
+ else if (id == "INT" || id == "INTEGER" || id == "INT4")
+ {
+ r.type = sql_type::INT;
+ }
+ else if (id == "BIGINT" || id == "INT8")
+ {
+ r.type = sql_type::BIGINT;
+ }
+ else if (id == "SERIAL")
+ {
+ r.type = sql_type::BIGINT;
+ r.unsign = true;
+ }
+ else if (id == "FLOAT" || id == "FLOAT4")
+ {
+ r.type = sql_type::FLOAT;
+ }
+ else if (id == "DOUBLE" || id == "FLOAT8")
+ {
+ r.type = sql_type::DOUBLE;
+ }
+ else if (id == "DECIMAL" ||
+ id == "DEC" ||
+ id == "NUMERIC" ||
+ id == "FIXED")
+ {
+ r.type = sql_type::DECIMAL;
+ }
+ //
+ // Date-time types.
+ //
+ else if (id == "DATE")
+ {
+ r.type = sql_type::DATE;
+ }
+ else if (id == "TIME")
+ {
+ r.type = sql_type::TIME;
+ }
+ else if (id == "DATETIME")
+ {
+ r.type = sql_type::DATETIME;
+ }
+ else if (id == "TIMESTAMP")
+ {
+ r.type = sql_type::TIMESTAMP;
+ }
+ else if (id == "YEAR")
+ {
+ r.type = sql_type::YEAR;
+ }
+ //
+ // String and binary types.
+ //
+ else if (id == "NCHAR")
+ {
+ r.type = sql_type::CHAR;
+ }
+ else if (id == "VARCHAR")
+ {
+ r.type = prefix == "LONG"
+ ? sql_type::MEDIUMTEXT
+ : sql_type::VARCHAR;
+ }
+ else if (id == "NVARCHAR")
+ {
+ r.type = sql_type::VARCHAR;
+ }
+ else if (id == "VARYING" && prefix == "CHARACTER")
+ {
+ r.type = sql_type::VARCHAR;
+ }
+ else if (id == "BINARY")
+ {
+ r.type = sql_type::BINARY;
+ }
+ else if (id == "BYTE" && prefix == "CHAR")
+ {
+ r.type = sql_type::BINARY;
+ }
+ else if (id == "VARBINARY")
+ {
+ r.type = prefix == "LONG"
+ ? sql_type::MEDIUMBLOB
+ : sql_type::VARBINARY;
+ }
+ else if (id == "TINYBLOB")
+ {
+ r.type = sql_type::TINYBLOB;
+ }
+ else if (id == "TINYTEXT")
+ {
+ r.type = sql_type::TINYTEXT;
+ }
+ else if (id == "BLOB")
+ {
+ r.type = sql_type::BLOB;
+ }
+ else if (id == "TEXT")
+ {
+ r.type = sql_type::TEXT;
+ }
+ else if (id == "MEDIUMBLOB")
+ {
+ r.type = sql_type::MEDIUMBLOB;
+ }
+ else if (id == "MEDIUMTEXT")
+ {
+ r.type = sql_type::MEDIUMTEXT;
+ }
+ else if (id == "LONGBLOB")
+ {
+ r.type = sql_type::LONGBLOB;
+ }
+ else if (id == "LONGTEXT")
+ {
+ r.type = sql_type::LONGTEXT;
+ }
+ else if (id == "ENUM")
+ {
+ r.type = sql_type::ENUM;
+ }
+ else if (id == "SET")
+ {
+ r.type = sql_type::SET;
+ }
+ else
+ match = false;
+
+ if (match)
+ {
+ s = parse_range;
+ continue;
+ }
+ }
+
+ // Some prefixes can also be type names if not followed
+ // by the actual type name.
+ //
+ if (!prefix.empty ())
+ {
+ if (prefix == "CHAR" || prefix == "CHARACTER")
+ {
+ r.type = sql_type::CHAR;
+ }
+ else if (prefix == "LONG")
+ {
+ r.type = sql_type::MEDIUMTEXT;
+ }
+ }
+
+ if (r.type == sql_type::invalid)
+ {
+ cerr << m.file () << ":" << m.line () << ":" <<
+ m.column () << ":";
+
+ if (tt == sql_token::t_identifier)
+ cerr << " error: unknown MySQL type '" <<
+ t.identifier () << "'" << endl;
+ else
+ cerr << " error: expected MySQL type name" << endl;
+
+ throw generation_failed ();
+ }
+
+ // Fall through.
+ //
+ s = parse_range;
+ }
+ case parse_range:
+ {
+ if (t.punctuation () == sql_token::p_lparen)
+ {
+ t = l.next ();
+
+ // ENUM and SET have a list of members instead of the range.
+ //
+ if (r.type == sql_type::ENUM || r.type == sql_type::SET)
+ {
+ // Skip tokens until we get the closing paren.
+ //
+ while (t.type () != sql_token::t_eos &&
+ t.punctuation () != sql_token::p_rparen)
+ t = l.next ();
+ }
+ else
+ {
+ if (t.type () != sql_token::t_int_lit)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: integer range expected in MySQL type "
+ << "declaration" << endl;
+
+ throw generation_failed ();
+ }
+
+ unsigned int v;
+ istringstream is (t.literal ());
+
+ if (!(is >> v && is.eof ()))
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: invalid range value '" << t.literal ()
+ << "'in MySQL type declaration" << endl;
+
+ throw generation_failed ();
+ }
+
+ r.range = true;
+ r.range_value = v;
+
+ t = l.next ();
+
+ if (t.punctuation () == sql_token::p_comma)
+ {
+ // We have the second range value. Skip it.
+ //
+ l.next ();
+ t = l.next ();
+ }
+ }
+
+ if (t.punctuation () != sql_token::p_rparen)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: expected ')' in MySQL type declaration"
+ << endl;
+
+ throw generation_failed ();
+ }
+
+ s = parse_sign;
+ continue;
+ }
+
+ // Fall through.
+ //
+ s = parse_sign;
+ }
+ case parse_sign:
+ {
+ if (tt == sql_token::t_identifier &&
+ t.identifier () == "UNSIGNED")
+ {
+ r.unsign = true;
+ }
+
+ s = parse_done;
+ break;
+ }
+ case parse_done:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ if (s == parse_name && !prefix.empty ())
+ {
+ // Some prefixes can also be type names if not followed
+ // by the actual type name.
+ //
+ if (prefix == "CHAR" || prefix == "CHARACTER")
+ {
+ r.type = sql_type::CHAR;
+ }
+ else if (prefix == "LONG")
+ {
+ r.type = sql_type::MEDIUMTEXT;
+ }
+ }
+
+ if (r.type == sql_type::invalid)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: incomplete MySQL type declaration" << endl;
+
+ throw generation_failed ();
+ }
+
+ // If range is omitted for CHAR or BIT types, it defaults to 1.
+ //
+ if ((r.type == sql_type::CHAR || r.type == sql_type::BIT) && !r.range)
+ {
+ r.range = true;
+ r.range_value = 1;
+ }
+
+ return r;
+ }
+ catch (sql_lexer::invalid_input const& e)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: invalid MySQL type declaration: " << e.message
+ << endl;
+
+ throw generation_failed ();
+ }
+ }
+ }
+}
diff --git a/odb/relational/mysql/context.hxx b/odb/relational/mysql/context.hxx
new file mode 100644
index 0000000..b1de84c
--- /dev/null
+++ b/odb/relational/mysql/context.hxx
@@ -0,0 +1,127 @@
+// file : odb/relational/mysql/context.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_MYSQL_CONTEXT_HXX
+#define ODB_RELATIONAL_MYSQL_CONTEXT_HXX
+
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ namespace mysql
+ {
+ struct sql_type
+ {
+ // Keep the order in each block of types.
+ //
+ enum core_type
+ {
+ // Integral types.
+ //
+ TINYINT,
+ SMALLINT,
+ MEDIUMINT,
+ INT,
+ BIGINT,
+
+ // Float types.
+ //
+ FLOAT,
+ DOUBLE,
+ DECIMAL,
+
+ // Data-time types.
+ //
+ DATE,
+ TIME,
+ DATETIME,
+ TIMESTAMP,
+ YEAR,
+
+ // String and binary types.
+ //
+ CHAR,
+ BINARY,
+ VARCHAR,
+ VARBINARY,
+ TINYTEXT,
+ TINYBLOB,
+ TEXT,
+ BLOB,
+ MEDIUMTEXT,
+ MEDIUMBLOB,
+ LONGTEXT,
+ LONGBLOB,
+
+ // Other types.
+ //
+ BIT,
+ ENUM,
+ SET,
+
+ // Invalid type.
+ //
+ invalid
+ };
+
+ sql_type () : type (invalid), unsign (false), range (false) {}
+
+ core_type type;
+ bool unsign;
+ bool range;
+ unsigned int range_value; // MySQL max value is 2^32 - 1 (LONGBLOG/TEXT).
+ };
+
+ class context: public virtual relational::context
+ {
+ public:
+ sql_type const&
+ db_type (semantics::data_member&, string const& key_prefix = string ());
+
+ protected:
+ virtual bool
+ grow_impl (semantics::class_&);
+
+ virtual bool
+ grow_impl (semantics::data_member&);
+
+ virtual bool
+ grow_impl (semantics::data_member&, semantics::type&, string const&);
+
+ protected:
+ virtual string
+ quote_id_impl (string const&) const;
+
+ private:
+ struct data: base_context::data
+ {
+ data (std::ostream& os): base_context::data (os) {}
+
+ virtual string
+ column_type_impl (semantics::type&,
+ string const& type,
+ semantics::context&,
+ column_type_flags) const;
+ };
+
+ private:
+ data* data_;
+
+ public:
+ static context&
+ current ()
+ {
+ return dynamic_cast<context&> (base_context::current ());
+ }
+
+ context (std::ostream&, semantics::unit&, options_type const&);
+
+ protected:
+ context ();
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_MYSQL_CONTEXT_HXX
diff --git a/odb/relational/mysql/header.cxx b/odb/relational/mysql/header.cxx
new file mode 100644
index 0000000..5210677
--- /dev/null
+++ b/odb/relational/mysql/header.cxx
@@ -0,0 +1,166 @@
+// file : odb/relational/mysql/header.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/header.hxx>
+
+#include <odb/relational/mysql/common.hxx>
+#include <odb/relational/mysql/context.hxx>
+
+namespace relational
+{
+ namespace mysql
+ {
+ namespace header
+ {
+ namespace relational = relational::header;
+
+ struct image_member: relational::image_member, member_base
+ {
+ image_member (base const& x)
+ : member_base::base (x), // virtual base
+ base (x),
+ member_base (x),
+ member_image_type_ (base::type_override_,
+ base::fq_type_override_,
+ base::key_prefix_)
+ {
+ }
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ if (container (mi.t))
+ 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
+ traverse_composite (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << endl;
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ // Exchanged as strings. Can have up to 65 digits not counting
+ // '-' and '.'. If range is not specified, the default is 10.
+ //
+
+ /*
+ @@ Disabled.
+ os << "char " << mi.var << "value[" <<
+ (t.range ? t.range_value : 10) + 3 << "];"
+ */
+
+ os << image_type << " " << mi.var << "value;"
+ << "unsigned long " << mi.var << "size;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+
+ }
+
+ virtual void
+ traverse_short_string (member_info& mi)
+ {
+ // If range is not specified, the default buffer size is 255.
+ //
+ /*
+ @@ Disabled.
+ os << "char " << mi.var << "value[" <<
+ (t.range ? t.range_value : 255) + 1 << "];"
+ */
+
+ os << image_type << " " << mi.var << "value;"
+ << "unsigned long " << mi.var << "size;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "unsigned long " << mi.var << "size;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ // Valid range is 1 to 64.
+ //
+ unsigned int n (mi.st->range / 8 + (mi.st->range % 8 ? 1 : 0));
+
+ os << "unsigned char " << mi.var << "value[" << n << "];"
+ << "unsigned long " << mi.var << "size;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_enum (member_info& mi)
+ {
+ // Represented as string.
+ //
+ os << image_type << " " << mi.var << "value;"
+ << "unsigned long " << mi.var << "size;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_set (member_info& mi)
+ {
+ // Represented as string.
+ //
+ os << image_type << " " << mi.var << "value;"
+ << "unsigned long " << mi.var << "size;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ private:
+ string image_type;
+
+ member_image_type member_image_type_;
+ };
+ entry<image_member> image_member_;
+ }
+ }
+}
diff --git a/odb/relational/mysql/schema.cxx b/odb/relational/mysql/schema.cxx
new file mode 100644
index 0000000..19569e4
--- /dev/null
+++ b/odb/relational/mysql/schema.cxx
@@ -0,0 +1,53 @@
+// file : odb/relational/mysql/schema.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/schema.hxx>
+
+#include <odb/relational/mysql/common.hxx>
+#include <odb/relational/mysql/context.hxx>
+
+namespace relational
+{
+ namespace mysql
+ {
+ namespace schema
+ {
+ namespace relational = relational::schema;
+
+ //
+ // Create.
+ //
+
+ struct create_common: virtual relational::create_common
+ {
+ virtual void
+ create_post ()
+ {
+ os << ")";
+
+ string const& engine (options.mysql_engine ());
+
+ if (engine != "default")
+ os << endl
+ << " ENGINE=" << engine;
+
+ os << endl;
+ }
+ };
+
+ struct member_create: relational::member_create, create_common
+ {
+ member_create (base const& x): base (x) {}
+ };
+ entry<member_create> member_create_;
+
+ struct class_create: relational::class_create, create_common
+ {
+ class_create (base const& x): base (x) {}
+ };
+ entry<class_create> class_create_;
+ }
+ }
+}
diff --git a/odb/relational/mysql/source.cxx b/odb/relational/mysql/source.cxx
new file mode 100644
index 0000000..71f556a
--- /dev/null
+++ b/odb/relational/mysql/source.cxx
@@ -0,0 +1,899 @@
+// file : odb/relational/mysql/source.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/source.hxx>
+
+#include <odb/relational/mysql/common.hxx>
+#include <odb/relational/mysql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mysql
+ {
+ namespace source
+ {
+ namespace relational = relational::source;
+
+ namespace
+ {
+ const char* integer_buffer_types[] =
+ {
+ "MYSQL_TYPE_TINY",
+ "MYSQL_TYPE_SHORT",
+ "MYSQL_TYPE_LONG", // *_bind_param() doesn't support INT24.
+ "MYSQL_TYPE_LONG",
+ "MYSQL_TYPE_LONGLONG"
+ };
+
+ const char* float_buffer_types[] =
+ {
+ "MYSQL_TYPE_FLOAT",
+ "MYSQL_TYPE_DOUBLE"
+ };
+
+ const char* date_time_buffer_types[] =
+ {
+ "MYSQL_TYPE_DATE",
+ "MYSQL_TYPE_TIME",
+ "MYSQL_TYPE_DATETIME",
+ "MYSQL_TYPE_TIMESTAMP",
+ "MYSQL_TYPE_SHORT"
+ };
+
+ const char* char_bin_buffer_types[] =
+ {
+ "MYSQL_TYPE_STRING", // CHAR
+ "MYSQL_TYPE_BLOB", // BINARY,
+ "MYSQL_TYPE_STRING", // VARCHAR
+ "MYSQL_TYPE_BLOB", // VARBINARY
+ "MYSQL_TYPE_STRING", // TINYTEXT
+ "MYSQL_TYPE_BLOB", // TINYBLOB
+ "MYSQL_TYPE_STRING", // TEXT
+ "MYSQL_TYPE_BLOB", // BLOB
+ "MYSQL_TYPE_STRING", // MEDIUMTEXT
+ "MYSQL_TYPE_BLOB", // MEDIUMBLOB
+ "MYSQL_TYPE_STRING", // LONGTEXT
+ "MYSQL_TYPE_BLOB" // LONGBLOB
+ };
+ }
+
+ //
+ // bind
+ //
+
+ struct bind_member: relational::bind_member, member_base
+ {
+ bind_member (base const& x)
+ : member_base::base (x), // virtual base
+ base (x),
+ member_base (x)
+ {
+ }
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ if (container (mi.t))
+ return false;
+
+ ostringstream ostr;
+ ostr << "b[n]";
+ b = ostr.str ();
+
+ 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 (var_override_.empty ())
+ {
+ if (semantics::class_* c = comp_value (mi.t))
+ os << "n += " << in_column_count (*c) << "UL;";
+ else
+ os << "n++;";
+
+ if (inverse (mi.m, key_prefix_))
+ os << "}";
+ else
+ os << endl;
+ }
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ os << "composite_value_traits< " << mi.fq_type () <<
+ " >::bind (b + n, " << arg << "." << mi.var << "value);";
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ // While the is_unsigned should indicate whether the
+ // buffer variable is unsigned, rather than whether the
+ // database type is unsigned, in case of the image types,
+ // this is the same.
+ //
+ os << b << ".buffer_type = " <<
+ integer_buffer_types[mi.st->type - sql_type::TINYINT] << ";"
+ << b << ".is_unsigned = " << (mi.st->unsign ? "1" : "0") << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << b << ".buffer_type = " <<
+ float_buffer_types[mi.st->type - sql_type::FLOAT] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ os << b << ".buffer_type = MYSQL_TYPE_NEWDECIMAL;"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
+ << b << ".buffer_length = static_cast<unsigned long> (" << endl
+ << "" << arg << "." << mi.var << "value.capacity ());"
+ << b << ".length = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << b << ".buffer_type = " <<
+ date_time_buffer_types[mi.st->type - sql_type::DATE] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;";
+
+ if (mi.st->type == sql_type::YEAR)
+ os << b << ".is_unsigned = 0;";
+
+ os << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_short_string (member_info& mi)
+ {
+ // MySQL documentation is quite confusing about the use of
+ // buffer_length and length when it comes to input parameters.
+ // Source code, however, tells us that it uses buffer_length
+ // only if length is NULL.
+ //
+ os << b << ".buffer_type = " <<
+ char_bin_buffer_types[mi.st->type - sql_type::CHAR] << ";"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
+ << b << ".buffer_length = static_cast<unsigned long> (" << endl
+ << "" << arg << "." << mi.var << "value.capacity ());"
+ << b << ".length = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << b << ".buffer_type = " <<
+ char_bin_buffer_types[mi.st->type - sql_type::CHAR] << ";"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
+ << b << ".buffer_length = static_cast<unsigned long> (" << endl
+ << "" << arg << "." << mi.var << "value.capacity ());"
+ << b << ".length = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ // Treated as a BLOB.
+ //
+ os << b << ".buffer_type = MYSQL_TYPE_BLOB;"
+ << b << ".buffer = " << arg << "." << mi.var << "value;"
+ << b << ".buffer_length = static_cast<unsigned long> (" << endl
+ << "sizeof (" << arg << "." << mi.var << "value));"
+ << b << ".length = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_enum (member_info& mi)
+ {
+ // Represented as a string.
+ //
+ os << b << ".buffer_type = MYSQL_TYPE_STRING;"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
+ << b << ".buffer_length = static_cast<unsigned long> (" << endl
+ << "" << arg << "." << mi.var << "value.capacity ());"
+ << b << ".length = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_set (member_info& mi)
+ {
+ // Represented as a string.
+ //
+ os << b << ".buffer_type = MYSQL_TYPE_STRING;"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
+ << b << ".buffer_length = static_cast<unsigned long> (" << endl
+ << "" << arg << "." << mi.var << "value.capacity ());"
+ << b << ".length = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ private:
+ string b;
+ string arg;
+ };
+ entry<bind_member> bind_member_;
+
+ //
+ // grow
+ //
+
+ struct grow_member: relational::grow_member, member_base
+ {
+ grow_member (base const& x)
+ : member_base::base (x), // virtual base
+ base (x),
+ member_base (x)
+ {
+ }
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ if (container (mi.t))
+ return false;
+
+ ostringstream ostr;
+ ostr << "e[" << index_ << "UL]";
+ e = ostr.str ();
+
+ if (var_override_.empty ())
+ os << "// " << mi.m.name () << endl
+ << "//" << endl;
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ if (semantics::class_* c = comp_value (mi.t))
+ index_ += in_column_count (*c);
+ else
+ index_++;
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ os << "if (composite_value_traits< " << mi.fq_type () <<
+ " >::grow (" << endl
+ << "i." << mi.var << "value, e + " << index_ << "UL))"
+ << "{"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_integer (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ // @@ Optimization disabled.
+ //
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_date_time (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_short_string (member_info& mi)
+ {
+ // @@ Optimization disabled.
+ //
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_bit (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_enum (member_info& mi)
+ {
+ // Represented as a string.
+ //
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_set (member_info& mi)
+ {
+ // Represented as a string.
+ //
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ private:
+ string e;
+ };
+ entry<grow_member> grow_member_;
+
+ //
+ // init image
+ //
+
+ struct init_image_member: relational::init_image_member, member_base
+ {
+ init_image_member (base const& x)
+ : member_base::base (x), // virtual base
+ base (x),
+ member_base (x),
+ member_image_type_ (base::type_override_,
+ base::fq_type_override_,
+ base::key_prefix_),
+ member_database_type_ (base::type_override_,
+ base::fq_type_override_,
+ base::key_prefix_)
+ {
+ }
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ // 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_;
+ else
+ {
+ string const& name (mi.m.name ());
+ member = "o." + name;
+
+ os << "// " << name << endl
+ << "//" << endl;
+ }
+
+ if (comp_value (mi.t))
+ traits = "composite_value_traits< " + mi.fq_type () + " >";
+ else
+ {
+ // When handling a pointer, mi.t is the id type of the referenced
+ // object.
+ //
+ semantics::type& mt (member_type (mi.m, key_prefix_));
+
+ if (semantics::class_* c = object_pointer (mt))
+ {
+ type = "obj_traits::id_type";
+ image_type = member_image_type_.image_type (mi.m);
+ db_type = member_database_type_.database_type (mi.m);
+
+ // Handle NULL pointers and extract the id.
+ //
+ os << "{"
+ << "typedef object_traits< " << c->fq_name () <<
+ " > obj_traits;";
+
+ if (weak_pointer (mt))
+ {
+ os << "typedef pointer_traits< " << mi.fq_type () <<
+ " > wptr_traits;"
+ << "typedef pointer_traits< wptr_traits::" <<
+ "strong_pointer_type > ptr_traits;"
+ << endl
+ << "wptr_traits::strong_pointer_type sp (" <<
+ "wptr_traits::lock (" << member << "));";
+
+ member = "sp";
+ }
+ else
+ os << "typedef pointer_traits< " << mi.fq_type () <<
+ " > ptr_traits;"
+ << endl;
+
+ os << "bool is_null (ptr_traits::null_ptr (" << member << "));"
+ << "if (!is_null)"
+ << "{"
+ << "const " << type << "& id (" << endl;
+
+ if (lazy_pointer (mt))
+ os << "ptr_traits::object_id< ptr_traits::element_type > (" <<
+ member << ")";
+ else
+ os << "obj_traits::id (ptr_traits::get_ref (" << member << "))";
+
+ os << ");"
+ << endl;
+
+ member = "id";
+ }
+ else
+ {
+ type = mi.fq_type ();
+ image_type = member_image_type_.image_type (mi.m);
+ db_type = member_database_type_.database_type (mi.m);
+
+ os << "{"
+ << "bool is_null;";
+ }
+
+ traits = "mysql::value_traits<\n "
+ + type + ",\n "
+ + image_type + ",\n "
+ + db_type + " >";
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ if (!comp_value (mi.t))
+ {
+ // When handling a pointer, mi.t is the id type of the referenced
+ // object.
+ //
+ if (object_pointer (member_type (mi.m, key_prefix_)))
+ {
+ os << "}";
+
+ if (!null_pointer (mi.m, key_prefix_))
+ os << "else" << endl
+ << "throw null_pointer ();";
+ }
+
+ os << "i." << mi.var << "null = is_null;"
+ << "}";
+ }
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ os << "if (" << traits << "::init (i." << mi.var << "value, " <<
+ member << "))"
+ << "{"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");";
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");";
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ // @@ Optimization: can remove growth check if buffer is fixed.
+ //
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size = static_cast<unsigned long> (size);"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");";
+ }
+
+ virtual void
+ traverse_short_string (member_info& mi)
+ {
+ // @@ Optimization: can remove growth check if buffer is fixed.
+ //
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size = static_cast<unsigned long> (size);"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size = static_cast<unsigned long> (size);"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ // Represented as a BLOB.
+ //
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "sizeof (i." << mi.var << "value)," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size = static_cast<unsigned long> (size);";
+ }
+
+ virtual void
+ traverse_enum (member_info& mi)
+ {
+ // Represented as a string.
+ //
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size = static_cast<unsigned long> (size);"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_set (member_info& mi)
+ {
+ // Represented as a string.
+ //
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size = static_cast<unsigned long> (size);"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ private:
+ string type;
+ string db_type;
+ string member;
+ string image_type;
+ string traits;
+
+ member_image_type member_image_type_;
+ member_database_type member_database_type_;
+ };
+ entry<init_image_member> init_image_member_;
+
+ //
+ // init value
+ //
+
+ struct init_value_member: relational::init_value_member, member_base
+ {
+ init_value_member (base const& x)
+ : member_base::base (x), // virtual base
+ base (x),
+ member_base (x),
+ member_image_type_ (base::type_override_,
+ base::fq_type_override_,
+ base::key_prefix_),
+ member_database_type_ (base::type_override_,
+ base::fq_type_override_,
+ base::key_prefix_)
+ {
+ }
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ if (container (mi.t))
+ return false;
+
+ if (!member_override_.empty ())
+ member = member_override_;
+ else
+ {
+ string const& name (mi.m.name ());
+ member = "o." + name;
+
+ os << "// " << name << endl
+ << "//" << endl;
+ }
+
+ if (comp_value (mi.t))
+ traits = "composite_value_traits< " + mi.fq_type () + " >";
+ else
+ {
+ // When handling a pointer, mi.t is the id type of the referenced
+ // object.
+ //
+ semantics::type& mt (member_type (mi.m, key_prefix_));
+
+ if (semantics::class_* c = object_pointer (mt))
+ {
+ type = "obj_traits::id_type";
+ image_type = member_image_type_.image_type (mi.m);
+ db_type = member_database_type_.database_type (mi.m);
+
+ // Handle NULL pointers and extract the id.
+ //
+ os << "{"
+ << "typedef object_traits< " << c->fq_name () <<
+ " > obj_traits;"
+ << "typedef pointer_traits< " << mi.fq_type () <<
+ " > ptr_traits;"
+ << endl
+ << "if (i." << mi.var << "null)" << endl;
+
+ if (null_pointer (mi.m, key_prefix_))
+ os << member << " = ptr_traits::pointer_type ();";
+ else
+ os << "throw null_pointer ();";
+
+ os << "else"
+ << "{"
+ << type << " id;";
+
+ member = "id";
+ }
+ else
+ {
+ type = mi.fq_type ();
+ image_type = member_image_type_.image_type (mi.m);
+ db_type = member_database_type_.database_type (mi.m);
+ }
+
+ traits = "mysql::value_traits<\n "
+ + type + ",\n "
+ + image_type + ",\n "
+ + db_type + " >";
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ if (comp_value (mi.t))
+ return;
+
+ // When handling a pointer, mi.t is the id type of the referenced
+ // object.
+ //
+ semantics::type& mt (member_type (mi.m, key_prefix_));
+
+ if (object_pointer (mt))
+ {
+ member = member_override_.empty ()
+ ? "o." + mi.m.name ()
+ : member_override_;
+
+ if (lazy_pointer (mt))
+ os << member << " = ptr_traits::pointer_type (db, id);";
+ else
+ os << "// If a compiler error points to the line below, then" << endl
+ << "// it most likely means that a pointer used in a member" << endl
+ << "// cannot be initialized from an object pointer." << endl
+ << "//" << endl
+ << member << " = ptr_traits::pointer_type (" << endl
+ << "db.load< ptr_traits::element_type > (id));";
+
+ os << "}"
+ << "}";
+ }
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ os << traits << "::init (" << member << ", i." <<
+ mi.var << "value, db);"
+ << endl;
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << ", i." << mi.var << "value, " <<
+ "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << ", i." << mi.var << "value, " <<
+ "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << ", i." << mi.var << "value, " <<
+ "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_short_string (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ // Represented as a BLOB.
+ //
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_enum (member_info& mi)
+ {
+ // Represented as a string.
+ //
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_set (member_info& mi)
+ {
+ // Represented as a string.
+ //
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ private:
+ string type;
+ string db_type;
+ string image_type;
+ string traits;
+ string member;
+
+ member_image_type member_image_type_;
+ member_database_type member_database_type_;
+ };
+ entry<init_value_member> init_value_member_;
+ }
+ }
+}
diff --git a/odb/relational/schema.cxx b/odb/relational/schema.cxx
new file mode 100644
index 0000000..0836e03
--- /dev/null
+++ b/odb/relational/schema.cxx
@@ -0,0 +1,101 @@
+// file : odb/relational/schema.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/emitter.hxx>
+
+#include <odb/relational/schema.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace schema
+ {
+ struct schema_emitter: emitter, context
+ {
+ virtual void
+ pre ()
+ {
+ first_ = true;
+ }
+
+ virtual void
+ line (const std::string& l)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << endl;
+
+ os << l;
+ }
+
+ virtual void
+ post ()
+ {
+ os << ';' << endl
+ << endl;
+ }
+
+ private:
+ bool first_;
+ };
+
+ static char const file_header[] =
+ "/* This file was generated by ODB, object-relational mapping (ORM)\n"
+ " * compiler for C++.\n"
+ " */\n\n";
+
+ void
+ generate ()
+ {
+ context ctx;
+ ostream& os (ctx.os);
+
+ os << file_header;
+
+ schema_emitter emitter;
+
+ // Drop.
+ //
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ traversal::namespace_ ns;
+ instance<class_drop> c (emitter);
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+
+ traversal::defines ns_defines;
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ unit.dispatch (ctx.unit);
+ }
+
+ os << endl;
+
+ // Create.
+ //
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ traversal::namespace_ ns;
+ instance<class_create> c (emitter);
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+
+ traversal::defines ns_defines;
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ unit.dispatch (ctx.unit);
+ }
+ }
+ }
+}
diff --git a/odb/relational/schema.hxx b/odb/relational/schema.hxx
new file mode 100644
index 0000000..bccb2a9
--- /dev/null
+++ b/odb/relational/schema.hxx
@@ -0,0 +1,379 @@
+// file : odb/relational/schema.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_SCHEMA_HXX
+#define ODB_RELATIONAL_SCHEMA_HXX
+
+#include <set>
+#include <cassert>
+
+#include <odb/emitter.hxx>
+
+#include <odb/relational/common.hxx>
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ namespace schema
+ {
+ typedef std::set<std::string> tables;
+
+ struct common: virtual context
+ {
+ common (emitter& e, ostream& os): e_ (e), os_ (os) {}
+
+ void
+ pre_statement ()
+ {
+ e_.pre ();
+ diverge (os_);
+ }
+
+ void
+ post_statement ()
+ {
+ restore ();
+ e_.post ();
+ }
+
+ protected:
+ emitter& e_;
+ ostream& os_;
+ };
+
+ //
+ // Drop.
+ //
+
+ struct drop_common: virtual context
+ {
+ virtual void
+ drop (string const& table)
+ {
+ os << "DROP TABLE IF EXISTS " << quote_id (table) << endl;
+ }
+ };
+
+ struct member_drop: object_members_base, common, virtual drop_common
+ {
+ typedef member_drop base;
+
+ member_drop (emitter& e, ostream& os, tables& t)
+ : object_members_base (false, true), common (e, os), tables_ (t)
+ {
+ }
+
+ virtual void
+ container (semantics::data_member& m)
+ {
+ // Ignore inverse containers of object pointers.
+ //
+ if (inverse (m, "value"))
+ return;
+
+ string const& name (table_name (m, table_prefix_));
+
+ if (tables_.count (name))
+ return;
+
+ pre_statement ();
+ drop (name);
+ post_statement ();
+
+ tables_.insert (name);
+ }
+
+ protected:
+ tables& tables_;
+ };
+
+ struct class_drop: traversal::class_, common, virtual drop_common
+ {
+ typedef class_drop base;
+
+ class_drop (emitter& e)
+ : common (e, os_), os_ (e), member_drop_ (e, os_, tables_)
+ {
+ }
+
+ class_drop (class_drop const& x)
+ : root_context (), //@@ -Wextra
+ context (),
+ common (x.e_, os_), os_ (x.e_), member_drop_ (x.e_, os_, tables_)
+ {
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ if (c.file () != unit.file ())
+ return;
+
+ if (!c.count ("object"))
+ return;
+
+ string const& name (table_name (c));
+
+ if (tables_.count (name))
+ return;
+
+ pre_statement ();
+ drop (name);
+ post_statement ();
+
+ tables_.insert (name);
+
+ // Drop tables for members.
+ //
+ member_drop_->traverse (c);
+ }
+
+ protected:
+ tables tables_;
+ emitter_ostream os_;
+ instance<member_drop> member_drop_;
+ };
+
+ //
+ // Create.
+ //
+
+ struct object_columns: object_columns_base, virtual context
+ {
+ typedef object_columns base;
+
+ object_columns (string const& prefix = string ())
+ : prefix_ (prefix)
+ {
+ }
+
+ 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;
+
+ os << " " << quote_id (name) << " " << column_type (m, prefix_);
+
+ if (m.count ("id"))
+ os << " PRIMARY KEY";
+
+ if (semantics::class_* c = object_pointer (member_type (m, prefix_)))
+ {
+ os << " REFERENCES " << quote_id (table_name (*c)) << " (" <<
+ quote_id (column_name (id_member (*c))) << ")";
+ }
+
+ return true;
+ }
+
+ protected:
+ string prefix_;
+ };
+
+ struct create_common: virtual context
+ {
+ virtual void
+ create_pre (string const& table)
+ {
+ os << "CREATE TABLE " << quote_id (table) << " (" << endl;
+ }
+
+ virtual void
+ index (string const& column)
+ {
+ os << "INDEX (" << quote_id (column) << ")";
+ }
+
+ virtual void
+ create_post ()
+ {
+ os << ")" << endl;
+ }
+ };
+
+
+ struct member_create: object_members_base, common, virtual create_common
+ {
+ typedef member_create base;
+
+ member_create (emitter& e, ostream& os, tables& t)
+ : object_members_base (false, true), common (e, os), tables_ (t)
+ {
+ }
+
+ virtual void
+ container (semantics::data_member& m)
+ {
+ using semantics::type;
+ using semantics::data_member;
+
+ // Ignore inverse containers of object pointers.
+ //
+ if (inverse (m, "value"))
+ return;
+
+ type& t (m.type ());
+ container_kind_type ck (container_kind (t));
+ type& vt (container_vt (t));
+
+ string const& name (table_name (m, table_prefix_));
+
+ if (tables_.count (name))
+ return;
+
+ pre_statement ();
+ create_pre (name);
+
+ // object_id (simple value)
+ //
+ string id_name (column_name (m, "id", "object_id"));
+ {
+ instance<object_columns> oc ("id");
+ oc->column (m, id_name, true);
+ }
+
+ // index (simple value)
+ //
+ string index_name;
+ bool ordered (ck == ck_ordered && !unordered (m));
+ if (ordered)
+ {
+ os << "," << endl;
+
+ instance<object_columns> oc ("index");
+ index_name = column_name (m, "index", "index");
+ oc->column (m, index_name, true);
+ }
+
+ // key (simple or composite value)
+ //
+ if (ck == ck_map || ck == ck_multimap)
+ {
+ type& kt (container_kt (t));
+
+ os << "," << endl;
+
+ if (semantics::class_* ckt = comp_value (kt))
+ {
+ instance<object_columns> oc;
+ oc->traverse_composite (m, *ckt, "key", "key");
+ }
+ else
+ {
+ instance<object_columns> oc ("key");
+ string const& name (column_name (m, "key", "key"));
+ oc->column (m, name, true);
+ }
+ }
+
+ // value (simple or composite value)
+ //
+ {
+ os << "," << endl;
+
+ if (semantics::class_* cvt = comp_value (vt))
+ {
+ instance<object_columns> oc;
+ oc->traverse_composite (m, *cvt, "value", "value");
+ }
+ else
+ {
+ instance<object_columns> oc ("value");
+ string const& name (column_name (m, "value", "value"));
+ oc->column (m, name, true);
+ }
+ }
+
+ // object_id index
+ //
+ os << "," << endl
+ << " ";
+ index (id_name);
+
+ // index index
+ //
+ if (ordered)
+ {
+ os << "," << endl
+ << " ";
+ index (index_name);
+ }
+
+ create_post ();
+ post_statement ();
+
+ tables_.insert (name);
+ }
+
+ protected:
+ tables& tables_;
+ };
+
+ struct class_create: traversal::class_, common, virtual create_common
+ {
+ typedef class_create base;
+
+ class_create (emitter& e)
+ : common (e, os_), os_ (e), member_create_ (e, os_, tables_)
+ {
+ }
+
+ class_create (class_create const& x)
+ : root_context (), //@@ -Wextra
+ context (),
+ common (x.e_, os_), os_ (x.e_), member_create_ (x.e_, os_, tables_)
+ {
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ if (c.file () != unit.file ())
+ return;
+
+ if (!c.count ("object"))
+ return;
+
+ string const& name (table_name (c));
+
+ // If the table with this name was already created, assume the
+ // user knows what they are doing and skip it.
+ //
+ if (tables_.count (name))
+ return;
+
+ pre_statement ();
+ create_pre (name);
+
+ {
+ instance<object_columns> oc;
+ oc->traverse (c);
+ }
+
+ create_post ();
+ post_statement ();
+
+ tables_.insert (name);
+
+ // Create tables for members.
+ //
+ member_create_->traverse (c);
+ }
+
+ protected:
+ tables tables_;
+ emitter_ostream os_;
+ instance<member_create> member_create_;
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_SCHEMA_HXX
diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx
new file mode 100644
index 0000000..be9da93
--- /dev/null
+++ b/odb/relational/source.cxx
@@ -0,0 +1,79 @@
+// file : odb/relational/source.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/source.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace source
+ {
+ void
+ generate ()
+ {
+ context ctx;
+ ostream& os (ctx.os);
+ options const& ops (ctx.options);
+
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ traversal::namespace_ ns;
+ class_ c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+
+ traversal::defines ns_defines;
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+
+ //
+ //
+ os << "#include <odb/cache-traits.hxx>" << endl;
+
+ if (ctx.embedded_schema)
+ os << "#include <odb/schema-catalog-impl.hxx>" << endl;
+
+ os << endl;
+
+ //
+ //
+ os << "#include <odb/mysql/mysql.hxx>" << endl
+ << "#include <odb/mysql/traits.hxx>" << endl
+ << "#include <odb/mysql/database.hxx>" << endl
+ << "#include <odb/mysql/transaction.hxx>" << endl
+ << "#include <odb/mysql/connection.hxx>" << endl
+ << "#include <odb/mysql/statement.hxx>" << endl
+ << "#include <odb/mysql/statement-cache.hxx>" << endl
+ << "#include <odb/mysql/object-statements.hxx>" << endl
+ << "#include <odb/mysql/container-statements.hxx>" << endl
+ << "#include <odb/mysql/exceptions.hxx>" << endl;
+
+ if (ops.generate_query ())
+ os << "#include <odb/mysql/result.hxx>" << endl;
+
+ os << endl;
+
+ // Details includes.
+ //
+ os << "#include <odb/details/unused.hxx>" << endl;
+
+ if (ops.generate_query ())
+ os << "#include <odb/details/shared-ptr.hxx>" << endl;
+
+ os << endl;
+
+ os << "namespace odb"
+ << "{";
+
+ unit.dispatch (ctx.unit);
+
+ os << "}";
+ }
+ }
+}
diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx
new file mode 100644
index 0000000..6f30007
--- /dev/null
+++ b/odb/relational/source.hxx
@@ -0,0 +1,2424 @@
+// file : odb/relational/source.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_SOURCE_HXX
+#define ODB_RELATIONAL_SOURCE_HXX
+
+#include <odb/gcc.hxx> // @@ ??
+
+#include <map>
+#include <set>
+#include <vector>
+#include <sstream>
+
+#include <odb/emitter.hxx>
+
+#include <odb/relational/context.hxx>
+#include <odb/relational/common.hxx>
+#include <odb/relational/schema.hxx>
+
+namespace relational
+{
+ namespace source
+ {
+ struct schema_emitter: emitter, virtual context
+ {
+ virtual void
+ pre ()
+ {
+ first_ = true;
+ os << "db.execute (";
+ }
+
+ virtual void
+ line (const std::string& l)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << endl;
+
+ os << strlit (l);
+ }
+
+ virtual void
+ post ()
+ {
+ os << ");" << endl;
+ }
+
+ private:
+ bool first_;
+ };
+
+ struct object_columns: object_columns_base, virtual context
+ {
+ typedef object_columns base;
+
+ object_columns (std::string const& table_name,
+ bool out,
+ char const* suffix = "")
+ : table_name_ (table_name),
+ out_ (out),
+ first_ (true),
+ suffix_ (suffix)
+ {
+ }
+
+ object_columns (std::string const& table_name,
+ bool out,
+ bool first,
+ char const* suffix = "")
+ : table_name_ (table_name),
+ out_ (out),
+ first_ (first),
+ suffix_ (suffix)
+ {
+ }
+
+ 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;
+
+ // Inverse object pointers come from a joined table.
+ //
+ if (im != 0)
+ {
+ semantics::class_* c (object_pointer (m.type ()));
+
+ 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, virtual context
+ {
+ typedef object_joins base;
+
+ //@@ context::object Might have to be create every time.
+ //
+ object_joins (semantics::class_& scope, bool query)
+ : 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& col_name, bool)
+ {
+ semantics::class_* c (object_pointer (m.type ()));
+
+ if (c == 0)
+ return true;
+
+ string t, dt;
+ std::ostringstream cond, dcond; // @@ diversion?
+
+ if (semantics::data_member* im = inverse (m))
+ {
+ if (container (im->type ()))
+ {
+ // This container is a direct member of the class so the table
+ // prefix is just the class table name.
+ //
+ string const& ct (table_name (*c));
+ table_prefix tp (ct + "_", 1);
+ t = table_name (*im, tp);
+ string const& val (column_name (*im, "value", "value"));
+
+ 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
+ {
+ 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));
+
+ 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 std::set<string> conditions;
+
+ struct join
+ {
+ string table;
+ conditions cond;
+
+ void
+ swap (join& o)
+ {
+ table.swap (o.table);
+ cond.swap (o.cond);
+ }
+ };
+
+ typedef std::vector<join> joins;
+ typedef std::map<string, size_t> table_map;
+
+ joins joins_;
+ table_map table_map_;
+ };
+
+ //
+ // bind
+ //
+
+ struct bind_member: virtual member_base
+ {
+ typedef bind_member base;
+
+ bind_member (string const& var = string (),
+ string const& arg = string ())
+ : member_base (var, 0, string (), string ()),
+ arg_override_ (arg)
+ {
+ }
+
+ bind_member (string const& var,
+ string const& arg,
+ semantics::type& t,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base (var, &t, fq_type, key_prefix),
+ arg_override_ (arg)
+ {
+ }
+
+ protected:
+ string arg_override_;
+ };
+
+ struct bind_base: traversal::class_, virtual context
+ {
+ typedef bind_base base;
+
+ virtual void
+ traverse (type& c)
+ {
+ // Ignore transient bases.
+ //
+ if (!(c.count ("object") || comp_value (c)))
+ return;
+
+ os << "// " << c.name () << " base" << endl
+ << "//" << endl
+ << "composite_value_traits< " << c.fq_name () <<
+ " >::bind (b + n, i);"
+ << "n += " << in_column_count (c) << "UL;"
+ << endl;
+ }
+ };
+
+ //
+ // grow
+ //
+
+ struct grow_member: virtual member_base
+ {
+ typedef grow_member base;
+
+ grow_member (size_t& index)
+ : member_base (string (), 0, string (), string ()), index_ (index)
+ {
+ }
+
+ grow_member (size_t& index,
+ string const& var,
+ semantics::type& t,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base (var, &t, fq_type, key_prefix), index_ (index)
+ {
+ }
+
+ protected:
+ size_t& index_;
+ };
+
+ struct grow_base: traversal::class_, virtual context
+ {
+ typedef grow_base base;
+
+ grow_base (size_t& index): index_ (index) {}
+
+ virtual void
+ traverse (type& c)
+ {
+ // Ignore transient bases.
+ //
+ if (!(c.count ("object") || comp_value (c)))
+ return;
+
+ os << "// " << c.name () << " base" << endl
+ << "//" << endl
+ << "if (composite_value_traits< " << c.fq_name () <<
+ " >::grow (i, e + " << index_ << "UL))"
+ << "{"
+ << "grew = true;"
+ << "}";
+
+ index_ += in_column_count (c);
+ }
+
+ protected:
+ size_t& index_;
+ };
+
+ //
+ // init image
+ //
+
+ struct init_image_member: virtual member_base
+ {
+ typedef init_image_member base;
+
+ init_image_member (string const& var = string (),
+ string const& member = string ())
+ : member_base (var, 0, string (), string ()),
+ member_override_ (member)
+ {
+ }
+
+ init_image_member (string const& var,
+ string const& member,
+ semantics::type& t,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base (var, &t, fq_type, key_prefix),
+ member_override_ (member)
+ {
+ }
+
+ protected:
+ string member_override_;
+ };
+
+ struct init_image_base: traversal::class_, virtual context
+ {
+ typedef init_image_base base;
+
+ virtual void
+ traverse (type& c)
+ {
+ // Ignore transient bases.
+ //
+ if (!(c.count ("object") || comp_value (c)))
+ return;
+
+ os << "// " << c.name () << " base" << endl
+ << "//" << endl
+ << "if (composite_value_traits< " << c.fq_name () <<
+ " >::init (i, o))"
+ << "{"
+ << "grew = true;"
+ << "}";
+ }
+ };
+
+ //
+ // init value
+ //
+
+ struct init_value_member: virtual member_base
+ {
+ typedef init_value_member base;
+
+ init_value_member (string const& member = string ())
+ : member_base (string (), 0, string (), string ()),
+ member_override_ (member)
+ {
+ }
+
+ init_value_member (string const& var,
+ string const& member,
+ semantics::type& t,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base (var, &t, fq_type, key_prefix),
+ member_override_ (member)
+ {
+ }
+
+ protected:
+ string member_override_;
+ };
+
+ struct init_value_base: traversal::class_, virtual context
+ {
+ typedef init_value_base base;
+
+ virtual void
+ traverse (type& c)
+ {
+ // Ignore transient bases.
+ //
+ if (!(c.count ("object") || comp_value (c)))
+ return;
+
+ os << "// " << c.name () << " base" << endl
+ << "//" << endl
+ << "composite_value_traits< " << c.fq_name () <<
+ " >::init (o, i, db);"
+ << endl;
+ }
+ };
+
+ // Member-specific traits types for container members.
+ //
+ struct container_traits: object_members_base, virtual context
+ {
+ typedef container_traits base;
+
+ container_traits (semantics::class_& obj) // @@ context::object
+ : object_members_base (true, true),
+ object_ (obj),
+ id_member_ (id_member (obj))
+ {
+ obj_scope_ = "access::object_traits< " + obj.fq_name () + " >";
+ }
+
+ virtual void
+ container (semantics::data_member& m)
+ {
+ using semantics::type;
+
+ type& t (m.type ());
+ container_kind_type ck (container_kind (t));
+
+ type& vt (container_vt (t));
+ type* it (0);
+ type* kt (0);
+
+ semantics::data_member* im (context::inverse (m, "value"));
+
+ bool ordered (false);
+ bool inverse (im != 0);
+ bool grow (false);
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (!unordered (m))
+ {
+ it = &container_it (t);
+ ordered = true;
+ grow = grow || context::grow (m, *it, "index");
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ kt = &container_kt (t);
+ grow = grow || context::grow (m, *kt, "key");
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ grow = grow || context::grow (m, vt, "value");
+
+ bool eager_ptr (is_a (m, test_eager_pointer, vt, "value") ||
+ has_a (vt, test_eager_pointer));
+
+ string name (prefix_ + public_name (m) + "_traits");
+ string scope (obj_scope_ + "::" + name);
+
+ os << "// " << m.name () << endl
+ << "//" << endl
+ << endl;
+
+ //
+ // Statements.
+ //
+ string table (table_name (m, table_prefix_));
+
+ // select_all_statement
+ //
+ os << "const char* const " << scope <<
+ "::select_all_statement =" << endl;
+
+ if (inverse)
+ {
+ semantics::class_* c (object_pointer (vt));
+
+ string inv_table; // Other table name.
+ string inv_id; // Other id column.
+ string inv_fid; // Other foreign id column (ref to us).
+
+ if (context::container (im->type ()))
+ {
+ // many(i)-to-many
+ //
+
+ // This other 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);
+ inv_table = table_name (*im, tp);
+ inv_id = column_name (*im, "id", "object_id");
+ inv_fid = column_name (*im, "value", "value");
+ }
+ else
+ {
+ // many(i)-to-one
+ //
+ inv_table = table_name (*c);
+ inv_id = column_name (id_member (*c));
+ inv_fid = column_name (*im);
+ }
+
+ os << "\"SELECT \"" << endl
+ << "\"`" << inv_fid << "`,\"" << endl
+ << "\"`" << inv_id << "`\"" << endl
+ << "\" FROM `" << inv_table << "` WHERE `" <<
+ inv_fid << "` = ?\"";
+ }
+ else
+ {
+ os << "\"SELECT \"" << endl
+ << "\"`" << column_name (m, "id", "object_id") << "`";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << ",\"" << endl
+ << "\"`" << column_name (m, "index", "index") << "`";
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ if (semantics::class_* ckt = comp_value (*kt))
+ {
+ instance<object_columns> t (table, false, false);
+ t->traverse_composite (m, *ckt, "key", "key");
+ }
+ else
+ {
+ os << ",\"" << endl
+ << "\"`" << column_name (m, "key", "key") << "`";
+ }
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ if (semantics::class_* cvt = comp_value (vt))
+ {
+ instance<object_columns> t (table, false, false);
+ t->traverse_composite (m, *cvt, "value", "value");
+ }
+ else
+ {
+ os << ",\"" << endl
+ << "\"`" << column_name (m, "value", "value") << "`";
+ }
+
+ os << "\"" << endl
+ << "\" FROM `" << table << "` WHERE `" <<
+ column_name (m, "id", "object_id") << "` = ?\"" << endl;
+
+ if (ordered)
+ os << "\" ORDER BY `" << column_name (m, "index", "index") <<
+ "`\"";
+ }
+
+ os << ";"
+ << endl;
+
+ // insert_one_statement
+ //
+ os << "const char* const " << scope <<
+ "::insert_one_statement =" << endl;
+
+ if (inverse)
+ os << " \"\";"
+ << endl;
+ else
+ {
+ os << "\"INSERT INTO `" << table << "` (\"" << endl
+ << "\"`" << column_name (m, "id", "object_id") << "`";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << ",\"" << endl
+ << "\"`" << column_name (m, "index", "index") << "`";
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ if (semantics::class_* ckt = comp_value (*kt))
+ {
+ instance<object_columns> t (table, false, false);
+ t->traverse_composite (m, *ckt, "key", "key");
+ }
+ else
+ {
+ os << ",\"" << endl
+ << "\"`" << column_name (m, "key", "key") << "`";
+ }
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ if (semantics::class_* cvt = comp_value (vt))
+ {
+ instance<object_columns> t (table, false, false);
+ t->traverse_composite (m, *cvt, "value", "value");
+ }
+ else
+ {
+ os << ",\"" << endl
+ << "\"`" << column_name (m, "value", "value") << "`";
+ }
+
+ os << "\"" << endl
+ << "\") VALUES (";
+
+ for (size_t i (0), n (m.get<size_t> ("data-column-count")); i < n; ++i)
+ os << (i != 0 ? "," : "") << '?';
+
+ os << ")\";"
+ << endl;
+ }
+
+ // delete_all_statement
+ //
+ os << "const char* const " << scope <<
+ "::delete_all_statement =" << endl;
+
+ if (inverse)
+ os << " \"\";"
+ << endl;
+ else
+ {
+ os << "\"DELETE FROM `" << table << "`\"" << endl
+ << "\" WHERE `" << column_name (m, "id", "object_id") << "` = ?\";"
+ << endl;
+ }
+
+ //
+ // Functions.
+ //
+
+ // bind()
+ //
+ {
+ instance<bind_member> bind_id ("id_", "id");
+
+ // bind (cond_image_type)
+ //
+ os << "void " << scope << "::" << endl
+ << "bind (MYSQL_BIND* b, id_image_type* p, cond_image_type& c)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (c);"
+ << endl
+ << "std::size_t n (0);"
+ << endl;
+
+ os << "// object_id" << endl
+ << "//" << endl
+ << "if (p != 0)"
+ << "{"
+ << "id_image_type& id (*p);";
+ bind_id->traverse (id_member_);
+ os << "}"
+ << "n++;"
+ << endl;
+
+ // We don't need to update the bind index since this is the
+ // last element.
+ //
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "index_", "c", *it, "index_type", "index");
+ bm->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<bind_member> bm ("key_", "c", *kt, "key_type", "key");
+ bm->traverse (m);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "// value" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "value_", "c", vt, "value_type", "value");
+ bm->traverse (m);
+ break;
+ }
+ }
+
+ os << "}";
+
+ // bind (data_image_type)
+ //
+ os << "void " << scope << "::" << endl
+ << "bind (MYSQL_BIND* b, id_image_type* p, data_image_type& d)"
+ << "{"
+ << "size_t n (0);"
+ << endl;
+
+ os << "// object_id" << endl
+ << "//" << endl
+ << "if (p != 0)"
+ << "{"
+ << "id_image_type& id (*p);";
+ bind_id->traverse (id_member_);
+ os << "}"
+ << "n++;"
+ << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "index_", "d", *it, "index_type", "index");
+ bm->traverse (m);
+ os << "n++;" // Simple value.
+ << endl;
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<bind_member> bm ("key_", "d", *kt, "key_type", "key");
+ bm->traverse (m);
+
+ if (semantics::class_* c = comp_value (*kt))
+ os << "n += " << in_column_count (*c) << "UL;"
+ << endl;
+ else
+ os << "n++;"
+ << endl;
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ // We don't need to update the bind index since this is the
+ // last element.
+ //
+ os << "// value" << endl
+ << "//" << endl;
+ instance<bind_member> bm ("value_", "d", vt, "value_type", "value");
+ bm->traverse (m);
+
+ os << "}";
+ }
+
+ // grow ()
+ //
+ {
+ size_t index (0);
+
+ os << "void " << scope << "::" << endl
+ << "grow (data_image_type& i, my_bool* e)"
+ << "{"
+ << "bool grew (false);"
+ << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<grow_member> gm (
+ index, "index_", *it, "index_type", "index");
+ gm->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<grow_member> gm (index, "key_", *kt, "key_type", "key");
+ gm->traverse (m);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ os << "// value" << endl
+ << "//" << endl;
+ instance<grow_member> gm (
+ index, "value_", vt, "value_type", "value");
+ gm->traverse (m);
+
+ os << "if (grew)" << endl
+ << "i.version++;"
+ << "}";
+ }
+
+ // init (data_image)
+ //
+ if (!inverse)
+ {
+ os << "void " << scope << "::" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "init (data_image_type& i, index_type j, " <<
+ "const value_type& v)";
+ else
+ os << "init (data_image_type& i, const value_type& v)";
+
+ os<< "{"
+ << "bool grew (false);"
+ << endl;
+
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+
+ instance<init_image_member> im (
+ "index_", "j", *it, "index_type", "index");
+ im->traverse (m);
+ }
+
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "init (data_image_type& i, const key_type& k, " <<
+ "const value_type& v)"
+ << "{"
+ << "bool grew (false);"
+ << endl
+ << "// key" << endl
+ << "//" << endl;
+
+ instance<init_image_member> im (
+ "key_", "k", *kt, "key_type", "key");
+ im->traverse (m);
+
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "init (data_image_type& i, const value_type& v)"
+ << "{"
+ << "bool grew (false);"
+ << endl;
+ break;
+ }
+ }
+
+ os << "// value" << endl
+ << "//" << endl;
+ {
+ instance<init_image_member> im (
+ "value_", "v", vt, "value_type", "value");
+ im->traverse (m);
+ }
+
+ os << "if (grew)" << endl
+ << "i.version++;"
+ << "}";
+ }
+
+ // init (data)
+ //
+ os << "void " << scope << "::" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "init (index_type& j, value_type& v, " <<
+ "const data_image_type& i, database& db)";
+ else
+ os << "init (value_type& v, const data_image_type& i, " <<
+ "database& db)";
+
+ os << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl;
+
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+
+ instance<init_value_member> im (
+ "index_", "j", *it, "index_type", "index");
+ im->traverse (m);
+ }
+
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "init (key_type& k, value_type& v, " <<
+ "const data_image_type& i, database& db)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl
+ << "// key" << endl
+ << "//" << endl;
+
+ instance<init_value_member> im (
+ "key_", "k", *kt, "key_type", "key");
+ im->traverse (m);
+
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "init (value_type& v, const data_image_type& i, " <<
+ "database& db)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl;
+ break;
+ }
+ }
+
+ os << "// value" << endl
+ << "//" << endl;
+ {
+ // If the value is an object pointer, pass the id type as a
+ // type override.
+ //
+ instance<init_value_member> im (
+ "value_", "v", vt, "value_type", "value");
+ im->traverse (m);
+ }
+ os << "}";
+
+ // insert_one
+ //
+ {
+ string ia, ka, va, da;
+
+ if (!inverse)
+ {
+ ia = ordered ? " i" : "";
+ ka = " k";
+ va = " v";
+ da = " d";
+ }
+
+ os << "void " << scope << "::" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "insert_one (index_type" << ia << ", " <<
+ "const value_type&" << va << ", " <<
+ "void*" << da << ")";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "insert_one (const key_type&" << ka << ", " <<
+ "const value_type&" << va << ", " <<
+ "void*" << da << ")";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "insert_one (const value_type&" << va << ", " <<
+ "void*" << da << ")";
+ break;
+ }
+ }
+
+ os << "{";
+
+ if (!inverse)
+ {
+ os << "using namespace mysql;"
+ << endl
+ << "typedef container_statements< " << name << " > statements;"
+ << "statements& sts (*static_cast< statements* > (d));"
+ << "binding& b (sts.data_image_binding ());"
+ << "data_image_type& di (sts.data_image ());"
+ << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "init (di, " << (ordered ? "i, " : "") << "v);";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "init (di, k, v);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "init (di, v);";
+ break;
+ }
+ }
+
+ os << endl
+ << "if (di.version != sts.data_image_version () || " <<
+ "b.version == 0)"
+ << "{"
+ << "bind (b.bind, 0, di);"
+ << "sts.data_image_version (di.version);"
+ << "b.version++;"
+ << "}"
+ << "if (!sts.insert_one_statement ().execute ())" << endl
+ << "throw object_already_persistent ();";
+ }
+
+ os << "}";
+ }
+
+
+ // load_all
+ //
+ os << "bool " << scope << "::" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "load_all (index_type&" << (ordered ? " i" : "") <<
+ ", value_type& v, void* d)";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "load_all (key_type& k, value_type& v, void* d)";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "load_all (value_type& v, void* d)";
+ break;
+ }
+ }
+
+ os << "{"
+ << "using namespace mysql;"
+ << endl
+ << "typedef container_statements< " << name << " > statements;"
+ << "statements& sts (*static_cast< statements* > (d));"
+ << "data_image_type& di (sts.data_image ());";
+
+ // Extract current element.
+ //
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "init (" << (ordered ? "i, " : "") <<
+ "v, di, sts.connection ().database ());"
+ << endl;
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "init (k, v, di, sts.connection ().database ());"
+ << endl;
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "init (v, di, sts.connection ().database ());"
+ << endl;
+ break;
+ }
+ }
+
+ // If we are loading an eager pointer, then the call to init
+ // above executes other statements which potentially could
+ // change the image.
+ //
+ if (eager_ptr)
+ {
+ os << "id_image_type& ii (sts.id_image ());"
+ << endl
+ << "if (di.version != sts.data_image_version () ||" << endl
+ << "ii.version != sts.data_id_image_version ())"
+ << "{"
+ << "binding& b (sts.data_image_binding ());"
+ << "bind (b.bind, &ii, di);"
+ << "sts.data_image_version (di.version);"
+ << "sts.data_id_image_version (ii.version);"
+ << "b.version++;"
+ << "}";
+ }
+
+ // Fetch next.
+ //
+ os << "select_statement& st (sts.select_all_statement ());"
+ << "select_statement::result r (st.fetch ());";
+
+ if (grow)
+ os << endl
+ << "if (r == select_statement::truncated)"
+ << "{"
+ << "grow (di, sts.data_image_error ());"
+ << endl
+ << "if (di.version != sts.data_image_version ())"
+ << "{"
+ << "binding& b (sts.data_image_binding ());"
+ << "bind (b.bind, 0, di);"
+ << "sts.data_image_version (di.version);"
+ << "b.version++;"
+ << "st.refetch ();"
+ << "}"
+ << "}";
+
+ os << "if (r == select_statement::no_data)"
+ << "{"
+ << "st.free_result ();"
+ << "return false;"
+ << "}"
+ << "return true;"
+ << "}";
+
+ // delete_all
+ //
+ os << "void " << scope << "::" << endl
+ << "delete_all (void*" << (inverse ? "" : " d") << ")"
+ << "{";
+
+ if (!inverse)
+ os << "using namespace mysql;"
+ << endl
+ << "typedef container_statements< " << name << " > statements;"
+ << "statements& sts (*static_cast< statements* > (d));"
+ << "sts.delete_all_statement ().execute ();";
+
+ os << "}";
+
+ // persist
+ //
+ if (!inverse)
+ {
+ os << "void " << scope << "::" << endl
+ << "persist (const container_type& c," << endl
+ << "id_image_type& id," << endl
+ << "statements_type& sts)"
+ << "{"
+ << "using namespace mysql;"
+ << endl
+ << "binding& b (sts.data_image_binding ());"
+ << "if (id.version != sts.data_id_image_version () || " <<
+ "b.version == 0)"
+ << "{"
+ << "bind (b.bind, &id, sts.data_image ());"
+ << "sts.data_id_image_version (id.version);"
+ << "b.version++;"
+ << "}"
+ << "sts.id_image (id);"
+ << "functions_type& fs (sts.functions ());";
+
+ if (ck == ck_ordered)
+ os << "fs.ordered (" << (ordered ? "true" : "false") << ");";
+
+ os << "container_traits::persist (c, fs);"
+ << "}";
+ }
+
+ // load
+ //
+ os << "void " << scope << "::" << endl
+ << "load (container_type& c," << endl
+ << "id_image_type& id," << endl
+ << "statements_type& sts)"
+ << "{"
+ << "using namespace mysql;"
+ << endl
+ << "binding& db (sts.data_image_binding ());"
+ << "if (id.version != sts.data_id_image_version () || db.version == 0)"
+ << "{"
+ << "bind (db.bind, &id, sts.data_image ());"
+ << "sts.data_id_image_version (id.version);"
+ << "db.version++;"
+ << "}"
+ << "binding& cb (sts.cond_image_binding ());"
+ << "if (id.version != sts.cond_id_image_version () || cb.version == 0)"
+ << "{"
+ << "bind (cb.bind, &id, sts.cond_image ());"
+ << "sts.cond_id_image_version (id.version);"
+ << "cb.version++;"
+ << "}"
+ << "select_statement& st (sts.select_all_statement ());"
+ << "st.execute ();";
+
+ // If we are loading eager object pointers, cache the result
+ // since we will be loading other objects.
+ //
+ if (eager_ptr)
+ os << "st.cache ();";
+
+ os << "select_statement::result r (st.fetch ());";
+
+ if (grow)
+ os << endl
+ << "if (r == select_statement::truncated)"
+ << "{"
+ << "data_image_type& di (sts.data_image ());"
+ << "grow (di, sts.data_image_error ());"
+ << endl
+ << "if (di.version != sts.data_image_version ())"
+ << "{"
+ << "bind (db.bind, 0, sts.data_image ());"
+ << "sts.data_image_version (di.version);"
+ << "db.version++;"
+ << "st.refetch ();"
+ << "}"
+ << "}";
+
+ os << "bool more (r != select_statement::no_data);"
+ << endl
+ << "if (!more)" << endl
+ << "st.free_result ();"
+ << endl
+ << "sts.id_image (id);"
+ << "functions_type& fs (sts.functions ());";
+
+ if (ck == ck_ordered)
+ os << "fs.ordered (" << (ordered ? "true" : "false") << ");";
+
+ os << "container_traits::load (c, more, fs);"
+ << "}";
+
+ // update
+ //
+ if (!inverse)
+ {
+ os << "void " << scope << "::" << endl
+ << "update (const container_type& c," << endl
+ << "id_image_type& id," << endl
+ << "statements_type& sts)"
+ << "{"
+ << "using namespace mysql;"
+ << endl
+ << "binding& db (sts.data_image_binding ());"
+ << "if (id.version != sts.data_id_image_version () || " <<
+ "db.version == 0)"
+ << "{"
+ << "bind (db.bind, &id, sts.data_image ());"
+ << "sts.data_id_image_version (id.version);"
+ << "db.version++;"
+ << "}"
+ //
+ // We may need cond if the specialization calls delete_all.
+ //
+ << "binding& cb (sts.cond_image_binding ());"
+ << "if (id.version != sts.cond_id_image_version () || " <<
+ "cb.version == 0)"
+ << "{"
+ << "bind (cb.bind, &id, sts.cond_image ());"
+ << "sts.cond_id_image_version (id.version);"
+ << "cb.version++;"
+ << "}"
+ << "sts.id_image (id);"
+ << "functions_type& fs (sts.functions ());";
+
+ if (ck == ck_ordered)
+ os << "fs.ordered (" << (ordered ? "true" : "false") << ");";
+
+ os << "container_traits::update (c, fs);"
+ << "}";
+ }
+
+ // erase
+ //
+ if (!inverse)
+ {
+ os << "void " << scope << "::" << endl
+ << "erase (id_image_type& id, statements_type& sts)"
+ << "{"
+ << "using namespace mysql;"
+ << endl
+ << "binding& b (sts.cond_image_binding ());"
+ << "if (id.version != sts.cond_id_image_version () || b.version == 0)"
+ << "{"
+ << "bind (b.bind, &id, sts.cond_image ());"
+ << "sts.cond_id_image_version (id.version);"
+ << "b.version++;"
+ << "}"
+ << "sts.id_image (id);"
+ << "functions_type& fs (sts.functions ());";
+
+ if (ck == ck_ordered)
+ os << "fs.ordered (" << (ordered ? "true" : "false") << ");";
+
+ os << "container_traits::erase (fs);"
+ << "}";
+ }
+ }
+
+ protected:
+ string obj_scope_;
+ semantics::class_& object_;
+ semantics::data_member& id_member_;
+ };
+
+ // Container statement cache members.
+ //
+ struct container_cache_members: object_members_base, virtual context
+ {
+ typedef container_cache_members base;
+
+ container_cache_members ()
+ : object_members_base (true, false)
+ {
+ }
+
+ virtual void
+ container (semantics::data_member& m)
+ {
+ string traits (prefix_ + public_name (m) + "_traits");
+ os << "mysql::container_statements< " << traits << " > " <<
+ prefix_ << m.name () << ";";
+ }
+ };
+
+ struct container_cache_init_members: object_members_base, virtual context
+ {
+ typedef container_cache_init_members base;
+
+ container_cache_init_members ()
+ : object_members_base (true, false), first_ (true)
+ {
+ }
+
+ virtual void
+ container (semantics::data_member& m)
+ {
+ if (first_)
+ {
+ os << endl
+ << ": ";
+ first_ = false;
+ }
+ else
+ os << "," << endl
+ << " ";
+
+ os << prefix_ << m.name () << " (c)";
+ }
+
+ protected:
+ bool first_;
+ };
+
+ // Calls for container members.
+ //
+ struct container_calls: object_members_base, virtual context
+ {
+ typedef container_calls base;
+
+ enum call_type
+ {
+ persist_call,
+ load_call,
+ update_call,
+ erase_call
+ };
+
+ container_calls (call_type call)
+ : object_members_base (true, false), call_ (call)
+ {
+ }
+
+ virtual void
+ composite (semantics::data_member& m, semantics::class_& c)
+ {
+ string old (obj_prefix_);
+ obj_prefix_ += m.name ();
+ obj_prefix_ += '.';
+ object_members_base::composite (m, c);
+ obj_prefix_ = old;
+ }
+
+ virtual void
+ container (semantics::data_member& m)
+ {
+ using semantics::type;
+
+ bool inverse (context::inverse (m, "value"));
+
+ string const& name (m.name ());
+ string obj_name (obj_prefix_ + name);
+ string sts_name (prefix_ + name);
+ string traits (prefix_ + public_name (m) + "_traits");
+
+ switch (call_)
+ {
+ case persist_call:
+ {
+ if (!inverse)
+ os << traits << "::persist (" << endl
+ << "obj." << obj_name << "," << endl
+ << "i," << endl
+ << "sts.container_statment_cache ()." << sts_name << ");"
+ << endl;
+ break;
+ }
+ case load_call:
+ {
+ os << traits << "::load (" << endl
+ << "obj." << obj_name << "," << endl
+ << "i," << endl
+ << "sts.container_statment_cache ()." << sts_name << ");"
+ << endl;
+ break;
+ }
+ case update_call:
+ {
+ if (!inverse)
+ os << traits << "::update (" << endl
+ << "obj." << obj_name << "," << endl
+ << "i," << endl
+ << "sts.container_statment_cache ()." << sts_name << ");"
+ << endl;
+ break;
+ }
+ case erase_call:
+ {
+ if (!inverse)
+ os << traits << "::erase (" << endl
+ << "i," << endl
+ << "sts.container_statment_cache ()." << sts_name << ");"
+ << endl;
+ break;
+ }
+ }
+ }
+
+ protected:
+ call_type call_;
+ string obj_prefix_;
+ };
+
+ //
+ //
+ struct class_: traversal::class_, virtual context
+ {
+ typedef class_ base;
+
+ class_ ()
+ : grow_base_ (index_),
+ grow_member_ (index_),
+ bind_id_member_ ("id_"),
+ init_id_image_member_ ("id_", "id"),
+ init_id_value_member_ ("id"),
+ schema_drop_ (schema_emitter_),
+ schema_create_ (schema_emitter_)
+ {
+ init ();
+ }
+
+ class_ (class_ const&)
+ : root_context (), //@@ -Wextra
+ context (),
+ grow_base_ (index_),
+ grow_member_ (index_),
+ bind_id_member_ ("id_"),
+ init_id_image_member_ ("id_", "id"),
+ init_id_value_member_ ("id"),
+ schema_drop_ (schema_emitter_),
+ schema_create_ (schema_emitter_)
+ {
+ init ();
+ }
+
+ void
+ init ()
+ {
+ grow_base_inherits_ >> grow_base_;
+ grow_member_names_ >> grow_member_;
+
+ bind_base_inherits_ >> bind_base_;
+ bind_member_names_ >> bind_member_;
+
+ init_image_base_inherits_ >> init_image_base_;
+ init_image_member_names_ >> init_image_member_;
+
+ init_value_base_inherits_ >> init_value_base_;
+ init_value_member_names_ >> init_value_member_;
+ }
+
+
+ virtual void
+ traverse (type& c)
+ {
+ if (c.file () != unit.file ())
+ return;
+
+ if (c.count ("object"))
+ traverse_object (c);
+ else if (comp_value (c))
+ traverse_value (c);
+ }
+
+ virtual void
+ traverse_object (type& c)
+ {
+ string const& type (c.fq_name ());
+ string traits ("access::object_traits< " + type + " >");
+
+ bool grow (context::grow (c));
+ bool def_ctor (TYPE_HAS_DEFAULT_CONSTRUCTOR (c.tree_node ()));
+
+ semantics::data_member& id (id_member (c));
+ bool auto_id (id.count ("auto"));
+ bool grow_id (context::grow (id));
+
+ os << "// " << c.name () << endl
+ << "//" << endl
+ << endl;
+
+ //
+ // Containers.
+ //
+ bool straight_containers (has_a (c, test_straight_container));
+ bool containers (straight_containers || has_a (c, test_container));
+
+ // Statement cache (definition).
+ //
+ {
+ os << "struct " << traits << "::container_statement_cache_type"
+ << "{";
+
+ instance<container_cache_members> cm;
+ cm->traverse (c);
+
+ os << (containers ? "\n" : "")
+ << "container_statement_cache_type (mysql::connection&" <<
+ (containers ? " c" : "") << ")";
+
+ instance<container_cache_init_members> im;
+ im->traverse (c);
+
+ os << "{"
+ << "}"
+ << "};";
+ }
+
+ // Traits types.
+ //
+ if (containers)
+ {
+ instance<container_traits> t (c);
+ t->traverse (c);
+ }
+
+ // query columns
+ //
+ if (options.generate_query ())
+ {
+ instance<query_columns> t (c);
+ t->traverse (c);
+ }
+
+ string const& table (table_name (c));
+
+ // persist_statement
+ //
+ os << "const char* const " << traits << "::persist_statement =" << endl
+ << "\"INSERT INTO `" << table << "` (\"" << endl;
+
+ {
+ instance<object_columns> t (table, false);
+ t->traverse (c);
+ }
+
+ os << "\"" << endl
+ << "\") VALUES (";
+
+ for (size_t i (0), n (in_column_count (c)); i < n; ++i)
+ os << (i != 0 ? "," : "") << '?';
+
+ os << ")\";"
+ << endl;
+
+ // find_statement
+ //
+ os << "const char* const " << traits << "::find_statement =" << endl
+ << "\"SELECT \"" << endl;
+
+ {
+ instance<object_columns> t (table, true);
+ t->traverse (c);
+ }
+
+ os << "\"" << endl
+ << "\" FROM `" << table << "`\"" << endl;
+
+ {
+ bool f (false);
+ instance<object_joins> t (c, f); // @@ (im)perfect forwarding
+ t->traverse (c);
+ t->write ();
+ }
+
+ os << "\" WHERE `" << table << "`.`" << column_name (id) << "` = ?\";"
+ << endl;
+
+ // update_statement
+ //
+ os << "const char* const " << traits << "::update_statement =" << endl
+ << "\"UPDATE `" << table << "` SET \"" << endl;
+
+ {
+ instance<object_columns> t (table, false, " = ?");
+ t->traverse (c);
+ }
+
+ os << "\"" << endl
+ << "\" WHERE `" << table << "`.`" << column_name (id) << "` = ?\";"
+ << endl;
+
+ // erase_statement
+ //
+ os << "const char* const " << traits << "::erase_statement =" << endl
+ << "\"DELETE FROM `" << table << "`\"" << endl
+ << "\" WHERE `" << table << "`.`" << column_name (id) << "` = ?\";"
+ << endl;
+
+ // query_clause
+ //
+ if (options.generate_query ())
+ {
+ bool t (true);
+ instance<object_joins> oj (c, t); //@@ (im)perfect forwarding
+ 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 " << (oj->count () ? "DISTINCT " : "") << "\""
+ << endl;
+
+ {
+ instance<object_columns> oc (table, true);
+ oc->traverse (c);
+ }
+
+ os << "\"" << endl
+ << "\" FROM `" << table << "`\"" << endl;
+
+ oj->write ();
+
+ os << "\" \";"
+ << endl;
+ }
+
+ // id
+ //
+ if (options.generate_query ())
+ {
+ os << traits << "::id_type" << endl
+ << traits << "::" << endl
+ << "id (const image_type& i)"
+ << "{"
+ << "id_type id;";
+ init_id_value_member_->traverse (id);
+ os << "return id;"
+ << "}";
+ }
+
+ // grow ()
+ //
+ os << "void " << traits << "::" << endl
+ << "grow (image_type& i, my_bool* e)"
+ << "{"
+ << "bool grew (false);"
+ << endl;
+
+ index_ = 0;
+ inherits (c, grow_base_inherits_);
+ names (c, grow_member_names_);
+
+ os << "if (grew)" << endl
+ << "i.version++;" << endl
+ << "}";
+
+ // bind (image_type)
+ //
+ os << "void " << traits << "::" << endl
+ << "bind (MYSQL_BIND* b, image_type& i, bool out)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (out);"
+ << endl
+ << "std::size_t n (0);"
+ << endl;
+
+ inherits (c, bind_base_inherits_);
+ names (c, bind_member_names_);
+
+ os << "}";
+
+ // bind (id_image_type)
+ //
+ os << "void " << traits << "::" << endl
+ << "bind (MYSQL_BIND* b, id_image_type& i)"
+ << "{"
+ << "std::size_t n (0);";
+ bind_id_member_->traverse (id);
+ os << "}";
+
+ // init (image, object)
+ //
+ os << "void " << traits << "::" << endl
+ << "init (image_type& i, const object_type& o)"
+ << "{"
+ << "bool grew (false);"
+ << endl;
+
+ inherits (c, init_image_base_inherits_);
+ names (c, init_image_member_names_);
+
+ os << "if (grew)" << endl
+ << "i.version++;" << endl
+ << "}";
+
+ // init (object, image)
+ //
+ os << "void " << traits << "::" << endl
+ << "init (object_type& o, const image_type& i, database& db)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl;
+
+ inherits (c, init_value_base_inherits_);
+ names (c, init_value_member_names_);
+
+ os << "}";
+
+ // init (id_image, id)
+ //
+ os << "void " << traits << "::" << endl
+ << "init (id_image_type& i, const id_type& id)"
+ << "{";
+
+ if (grow_id)
+ os << "bool grew (false);";
+
+ init_id_image_member_->traverse (id);
+
+ if (grow_id)
+ os << endl
+ << "if (grew)" << endl
+ << "i.version++;";
+
+ os << "}";
+
+ // persist ()
+ //
+ os << "void " << traits << "::" << endl
+ << "persist (database&, " << (auto_id ? "" : "const ") <<
+ "object_type& obj)"
+ << "{"
+ << "using namespace mysql;"
+ << endl
+ << "connection& conn (mysql::transaction::current ().connection ());"
+ << "object_statements< object_type >& sts (" << endl
+ << "conn.statement_cache ().find<object_type> ());"
+ << "image_type& im (sts.image ());"
+ << "binding& imb (sts.in_image_binding ());"
+ << endl;
+
+ if (auto_id)
+ os << "obj." << id.name () << " = 0;";
+
+ os << "init (im, obj);"
+ << endl
+ << "if (im.version != sts.in_image_version () || imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, false);"
+ << "sts.in_image_version (im.version);"
+ << "imb.version++;"
+ << "}"
+ << "insert_statement& st (sts.persist_statement ());"
+ << "if (!st.execute ())" << endl
+ << "throw object_already_persistent ();"
+ << endl;
+
+ if (auto_id)
+ os << "obj." << id.name () << " = static_cast<id_type> (st.id ());"
+ << endl;
+
+ if (straight_containers)
+ {
+ // Initialize id_image.
+ //
+ os << "id_image_type& i (sts.id_image ());"
+ << "init (i, obj." << id.name () << ");"
+ << endl;
+
+ instance<container_calls> t (container_calls::persist_call);
+ t->traverse (c);
+ }
+
+ os << "}";
+
+ // update ()
+ //
+ os << "void " << traits << "::" << endl
+ << "update (database&, const object_type& obj)"
+ << "{"
+ << "using namespace mysql;"
+ << endl
+ << "connection& conn (mysql::transaction::current ().connection ());"
+ << "object_statements< object_type >& sts (" << endl
+ << "conn.statement_cache ().find<object_type> ());"
+ << endl;
+
+ // Initialize id image.
+ //
+ os << "id_image_type& i (sts.id_image ());"
+ << "init (i, obj." << id.name () << ");"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;"
+ << "}";
+
+ // Initialize data image.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.in_image_binding ());"
+ << "init (im, obj);"
+ << endl
+ << "if (im.version != sts.in_image_version () || imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, false);"
+ << "sts.in_image_version (im.version);"
+ << "imb.version++;"
+ << "}"
+ << "sts.update_statement ().execute ();";
+
+ if (straight_containers)
+ {
+ os << endl;
+ instance<container_calls> t (container_calls::update_call);
+ t->traverse (c);
+ }
+
+ os << "}";
+
+ // erase ()
+ //
+ os << "void " << traits << "::" << endl
+ << "erase (database&, const id_type& id)"
+ << "{"
+ << "using namespace mysql;"
+ << endl
+ << "connection& conn (mysql::transaction::current ().connection ());"
+ << "object_statements< object_type >& sts (" << endl
+ << "conn.statement_cache ().find<object_type> ());"
+ << endl;
+
+ // Initialize id image.
+ //
+ os << "id_image_type& i (sts.id_image ());"
+ << "init (i, id);"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;"
+ << "}"
+ << "if (sts.erase_statement ().execute () != 1)" << endl
+ << "throw object_not_persistent ();";
+
+ if (straight_containers)
+ {
+ os << endl;
+ instance<container_calls> t (container_calls::erase_call);
+ t->traverse (c);
+ }
+
+ os << "}";
+
+ // find ()
+ //
+ if (def_ctor)
+ {
+ os << traits << "::pointer_type" << endl
+ << traits << "::" << endl
+ << "find (database& db, const id_type& id)"
+ << "{"
+ << "using namespace mysql;"
+ << endl
+ << "connection& conn (mysql::transaction::current ().connection ());"
+ << "object_statements< object_type >& sts (" << endl
+ << "conn.statement_cache ().find<object_type> ());"
+ << "object_statements< object_type >::auto_lock l (sts);"
+ << endl
+ << "if (l.locked ())"
+ << "{"
+ << "if (!find_ (sts, id))" << endl
+ << "return pointer_type ();"
+ << "}"
+ << "pointer_type p (" << endl
+ << "access::object_factory< object_type, pointer_type >::create ());"
+ << "pointer_traits< pointer_type >::guard pg (p);"
+ << "pointer_cache_traits< pointer_type >::insert_guard ig (" << endl
+ << "pointer_cache_traits< pointer_type >::insert (db, id, p));"
+ << "object_type& obj (pointer_traits< pointer_type >::get_ref (p));"
+ << endl
+ << "if (l.locked ())"
+ << "{"
+ << "init (obj, sts.image (), db);"
+ << "load_ (sts, obj);"
+ << "sts.load_delayed ();"
+ << "l.unlock ();"
+ << "}"
+ << "else" << endl
+ << "sts.delay_load (id, obj, ig.position ());"
+ << endl;
+
+ os << "ig.release ();"
+ << "pg.release ();"
+ << "return p;"
+ << "}";
+ }
+
+ os << "bool " << traits << "::" << endl
+ << "find (database& db, const id_type& id, object_type& obj)"
+ << "{"
+ << "using namespace mysql;"
+ << endl
+ << "connection& conn (mysql::transaction::current ().connection ());"
+ << "object_statements< object_type >& sts (" << endl
+ << "conn.statement_cache ().find<object_type> ());"
+ << "object_statements< object_type >::auto_lock l (sts);"
+ << endl
+ << "if (l.locked ())"
+ << "{"
+ << "if (!find_ (sts, id))" << endl
+ << "return false;"
+ << "}"
+ << "reference_cache_traits< object_type >::insert_guard ig (" << endl
+ << "reference_cache_traits< object_type >::insert (db, id, obj));"
+ << endl
+ << "if (l.locked ())"
+ << "{"
+ << "init (obj, sts.image (), db);"
+ << "load_ (sts, obj);"
+ << "sts.load_delayed ();"
+ << "l.unlock ();"
+ << "}"
+ << "else" << endl
+ << "sts.delay_load (id, obj, ig.position ());"
+ << endl;
+
+ os << "ig.release ();"
+ << "return true;"
+ << "}";
+
+ //
+ //
+ os << "bool " << traits << "::" << endl
+ << "find_ (mysql::object_statements< object_type >& sts, " <<
+ "const id_type& id)"
+ << "{"
+ << "using namespace mysql;"
+ << endl;
+
+ // Initialize id image.
+ //
+ os << "id_image_type& i (sts.id_image ());"
+ << "init (i, id);"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;"
+ << "}";
+
+ // Rebind data image.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.out_image_binding ());"
+ << endl
+ << "if (im.version != sts.out_image_version () || imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, true);"
+ << "sts.out_image_version (im.version);"
+ << "imb.version++;"
+ << "}"
+ << "select_statement& st (sts.find_statement ());"
+ << "st.execute ();"
+ << "select_statement::result r (st.fetch ());";
+
+ if (grow)
+ os << endl
+ << "if (r == select_statement::truncated)"
+ << "{"
+ << "grow (im, sts.out_image_error ());"
+ << endl
+ << "if (im.version != sts.out_image_version ())"
+ << "{"
+ << "bind (imb.bind, im, true);"
+ << "sts.out_image_version (im.version);"
+ << "imb.version++;"
+ << "st.refetch ();"
+ << "}"
+ << "}";
+
+ os << "st.free_result ();"
+ << "return r != select_statement::no_data;"
+ << "}";
+
+ // load_()
+ //
+ if (containers)
+ {
+ os << "void " << traits << "::" << endl
+ << "load_ (mysql::object_statements< object_type >& sts, " <<
+ "object_type& obj)"
+ << "{"
+ << "id_image_type& i (sts.id_image ());"
+ << endl;
+ instance<container_calls> t (container_calls::load_call);
+ t->traverse (c);
+ os << "}";
+ }
+
+ // query ()
+ //
+ if (options.generate_query ())
+ {
+ os << "template<>" << endl
+ << "result< " << traits << "::object_type >" << endl
+ << traits << "::" << endl
+ << "query< " << traits << "::object_type > (" << endl
+ << "database& db," << endl
+ << "const query_type& q)"
+ << "{"
+ << "using namespace mysql;"
+ << endl
+ << "connection& conn (mysql::transaction::current ().connection ());"
+ << endl
+ << "object_statements< object_type >& sts (" << endl
+ << "conn.statement_cache ().find<object_type> ());"
+ << "details::shared_ptr<select_statement> st;"
+ << endl
+ << "query_ (db, q, sts, st);"
+ << endl
+ << "details::shared_ptr<odb::result_impl<object_type> > r (" << endl
+ << "new (details::shared) mysql::result_impl<object_type> (st, sts));"
+ << "return result<object_type> (r);"
+ << "}";
+
+ os << "template<>" << endl
+ << "result< const " << traits << "::object_type >" << endl
+ << traits << "::" << endl
+ << "query< const " << traits << "::object_type > (" << endl
+ << "database& db," << endl
+ << "const query_type& q)"
+ << "{"
+ << "using namespace mysql;"
+ << endl
+ << "connection& conn (mysql::transaction::current ().connection ());"
+ << endl
+ << "object_statements< object_type >& sts (" << endl
+ << "conn.statement_cache ().find<object_type> ());"
+ << "details::shared_ptr<select_statement> st;"
+ << endl
+ << "query_ (db, q, sts, st);"
+ << endl
+ << "details::shared_ptr<odb::result_impl<const object_type> > r (" << endl
+ << "new (details::shared) mysql::result_impl<const object_type> (st, sts));"
+ << "return result<const object_type> (r);"
+ << "}";
+
+ os << "void " << traits << "::" << endl
+ << "query_ (database&," << endl
+ << "const query_type& q," << endl
+ << "mysql::object_statements< object_type >& sts,"
+ << "details::shared_ptr<mysql::select_statement>& st)"
+ << "{"
+ << "using namespace mysql;"
+ << endl
+ << "image_type& im (sts.image ());"
+ << "binding& imb (sts.out_image_binding ());"
+ << endl
+ << "if (im.version != sts.out_image_version () || imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, true);"
+ << "sts.out_image_version (im.version);"
+ << "imb.version++;"
+ << "}"
+ << "st.reset (new (details::shared) select_statement (" << endl
+ << "sts.connection ()," << endl
+ << "query_clause + q.clause ()," << endl
+ << "q.parameters ()," << endl
+ << "imb));"
+ << "st->execute ();"
+ << "}";
+ }
+
+ // create_schema ()
+ //
+ if (embedded_schema)
+ {
+ os << "void " << traits << "::" << endl
+ << "create_schema (database& db)"
+ << "{";
+
+ schema_drop_->traverse (c);
+ schema_create_->traverse (c);
+
+ os << "}";
+
+ os << "static const schema_catalog_entry" << endl
+ << "schema_catalog_entry_" << flat_name (type) << "_ (" << endl
+ << strlit (options.default_schema ()) << "," << endl
+ << "&" << traits << "::create_schema);"
+ << endl;
+ }
+ }
+
+ virtual void
+ traverse_value (type& c)
+ {
+ string const& type (c.fq_name ());
+ string traits ("access::composite_value_traits< " + type + " >");
+
+ os << "// " << c.name () << endl
+ << "//" << endl
+ << endl;
+
+ // grow ()
+ //
+ os << "bool " << traits << "::" << endl
+ << "grow (image_type& i, my_bool* e)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (e);"
+ << endl
+ << "bool grew (false);"
+ << endl;
+
+ index_ = 0;
+ inherits (c, grow_base_inherits_);
+ names (c, grow_member_names_);
+
+ os << "return grew;"
+ << "}";
+
+ // bind (image_type)
+ //
+ os << "void " << traits << "::" << endl
+ << "bind (MYSQL_BIND* b, image_type& i)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (b);"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << endl
+ << "std::size_t n (0);"
+ << "ODB_POTENTIALLY_UNUSED (n);"
+ << endl;
+
+ inherits (c, bind_base_inherits_);
+ names (c, bind_member_names_);
+
+ os << "}";
+
+ // init (image, object)
+ //
+ os << "bool " << traits << "::" << endl
+ << "init (image_type& i, const value_type& o)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (o);"
+ << endl
+ << "bool grew (false);"
+ << endl;
+
+ inherits (c, init_image_base_inherits_);
+ names (c, init_image_member_names_);
+
+ os << "return grew;"
+ << "}";
+
+ // init (object, image)
+ //
+ os << "void " << traits << "::" << endl
+ << "init (value_type& o, const image_type& i, database& db)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (o);"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl;
+
+ inherits (c, init_value_base_inherits_);
+ names (c, init_value_member_names_);
+
+ os << "}";
+ }
+
+ private:
+ bool id_;
+ size_t index_;
+
+ instance<grow_base> grow_base_;
+ traversal::inherits grow_base_inherits_;
+ instance<grow_member> grow_member_;
+ traversal::names grow_member_names_;
+
+ instance<bind_base> bind_base_;
+ traversal::inherits bind_base_inherits_;
+ instance<bind_member> bind_member_;
+ traversal::names bind_member_names_;
+ instance<bind_member> bind_id_member_;
+
+ instance<init_image_base> init_image_base_;
+ traversal::inherits init_image_base_inherits_;
+ instance<init_image_member> init_image_member_;
+ traversal::names init_image_member_names_;
+
+ instance<init_image_member> init_id_image_member_;
+
+ instance<init_value_base> init_value_base_;
+ traversal::inherits init_value_base_inherits_;
+ instance<init_value_member> init_value_member_;
+ traversal::names init_value_member_names_;
+ instance<init_value_member> init_id_value_member_;
+
+ schema_emitter schema_emitter_;
+ instance<schema::class_drop> schema_drop_;
+ instance<schema::class_create> schema_create_;
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_SOURCE_HXX