From 7ae497743c7b042904fe1f6b4153ab3f4763ff2b Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 10 Mar 2011 08:44:28 +0200 Subject: 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. --- odb/common.cxx | 22 +- odb/common.hxx | 65 +- odb/context.cxx | 71 +- odb/context.hxx | 100 +- odb/emitter.cxx | 16 + odb/generator.cxx | 26 +- odb/makefile | 24 +- odb/mysql/common.cxx | 566 ------- odb/mysql/common.hxx | 270 ---- odb/mysql/context.cxx | 637 -------- odb/mysql/context.hxx | 127 -- odb/mysql/header.cxx | 1031 ------------ odb/mysql/header.hxx | 17 - odb/mysql/inline.cxx | 129 -- odb/mysql/inline.hxx | 17 - odb/mysql/schema.cxx | 11 - odb/mysql/schema.hxx | 325 ---- odb/mysql/source.cxx | 3266 -------------------------------------- odb/mysql/source.hxx | 17 - odb/mysql/sql-schema.cxx | 98 -- odb/mysql/sql-schema.hxx | 17 - odb/relational/common.cxx | 178 +++ odb/relational/common.hxx | 303 ++++ odb/relational/context.cxx | 54 + odb/relational/context.hxx | 81 + odb/relational/context.ixx | 31 + odb/relational/generate.hxx | 36 + odb/relational/header.cxx | 54 + odb/relational/header.hxx | 886 +++++++++++ odb/relational/inline.cxx | 42 + odb/relational/inline.hxx | 107 ++ odb/relational/mysql/common.cxx | 448 ++++++ odb/relational/mysql/common.hxx | 226 +++ odb/relational/mysql/context.cxx | 642 ++++++++ odb/relational/mysql/context.hxx | 127 ++ odb/relational/mysql/header.cxx | 166 ++ odb/relational/mysql/schema.cxx | 53 + odb/relational/mysql/source.cxx | 899 +++++++++++ odb/relational/schema.cxx | 101 ++ odb/relational/schema.hxx | 379 +++++ odb/relational/source.cxx | 79 + odb/relational/source.hxx | 2424 ++++++++++++++++++++++++++++ odb/type-processor.cxx | 23 +- 43 files changed, 7571 insertions(+), 6620 deletions(-) delete mode 100644 odb/mysql/common.cxx delete mode 100644 odb/mysql/common.hxx delete mode 100644 odb/mysql/context.cxx delete mode 100644 odb/mysql/context.hxx delete mode 100644 odb/mysql/header.cxx delete mode 100644 odb/mysql/header.hxx delete mode 100644 odb/mysql/inline.cxx delete mode 100644 odb/mysql/inline.hxx delete mode 100644 odb/mysql/schema.cxx delete mode 100644 odb/mysql/schema.hxx delete mode 100644 odb/mysql/source.cxx delete mode 100644 odb/mysql/source.hxx delete mode 100644 odb/mysql/sql-schema.cxx delete mode 100644 odb/mysql/sql-schema.hxx create mode 100644 odb/relational/common.cxx create mode 100644 odb/relational/common.hxx create mode 100644 odb/relational/context.cxx create mode 100644 odb/relational/context.hxx create mode 100644 odb/relational/context.ixx create mode 100644 odb/relational/generate.hxx create mode 100644 odb/relational/header.cxx create mode 100644 odb/relational/header.hxx create mode 100644 odb/relational/inline.cxx create mode 100644 odb/relational/inline.hxx create mode 100644 odb/relational/mysql/common.cxx create mode 100644 odb/relational/mysql/common.hxx create mode 100644 odb/relational/mysql/context.cxx create mode 100644 odb/relational/mysql/context.hxx create mode 100644 odb/relational/mysql/header.cxx create mode 100644 odb/relational/mysql/schema.cxx create mode 100644 odb/relational/mysql/source.cxx create mode 100644 odb/relational/schema.cxx create mode 100644 odb/relational/schema.hxx create mode 100644 odb/relational/source.cxx create mode 100644 odb/relational/source.hxx diff --git a/odb/common.cxx b/odb/common.cxx index fe722df..342f28b 100644 --- a/odb/common.cxx +++ b/odb/common.cxx @@ -37,14 +37,23 @@ traverse_composite (semantics::data_member& m, semantics::class_& c) void object_members_base:: traverse (semantics::class_& c) { + bool obj (c.count ("object")); + // Ignore transient bases. // - if (!(c.count ("object") || context::comp_value (c))) + if (!(obj || context::comp_value (c))) return; - if (build_table_prefix_ && c.count ("object")) + semantics::class_* prev; + if (obj) + { + prev = object; + object = &c; + } + + if (obj && build_table_prefix_) { - table_prefix_.prefix = ctx_->table_name (c); + table_prefix_.prefix = table_name (c); table_prefix_.prefix += '_'; table_prefix_.level = 1; @@ -59,6 +68,9 @@ traverse (semantics::class_& c) inherits (c); names (c); } + + if (obj) + object = prev; } void object_members_base::member:: @@ -76,7 +88,7 @@ traverse (semantics::data_member& m) if (om_.build_prefix_) { old_prefix = om_.prefix_; - om_.prefix_ += om_.ctx_->public_name (m); + om_.prefix_ += om_.public_name (m); om_.prefix_ += '_'; } @@ -99,7 +111,7 @@ traverse (semantics::data_member& m) // else { - string name (om_.ctx_->public_name_db (m)); + string name (om_.public_name_db (m)); size_t n (name.size ()); om_.table_prefix_.prefix += name; diff --git a/odb/common.hxx b/odb/common.hxx index 9479b77..99741b2 100644 --- a/odb/common.hxx +++ b/odb/common.hxx @@ -13,7 +13,7 @@ // Traverse object members recursively by going into composite members. // -struct object_members_base: traversal::class_ +struct object_members_base: traversal::class_, virtual context { virtual void simple (semantics::data_member&); @@ -31,25 +31,21 @@ struct object_members_base: traversal::class_ public: object_members_base () - : ctx_ (0), - build_prefix_ (false), - build_table_prefix_ (false), - member_ (*this) + : member_ (*this) { - *this >> names_ >> member_; - *this >> inherits_ >> *this; + init (false, false); } - object_members_base (context& c, - bool build_prefix, - bool build_table_prefix) - : ctx_ (&c), - build_prefix_ (build_prefix), - build_table_prefix_ (build_table_prefix), - member_ (*this) + object_members_base (bool build_prefix, bool build_table_prefix) + : member_ (*this) { - *this >> names_ >> member_; - *this >> inherits_ >> *this; + init (build_prefix, build_table_prefix); + } + + object_members_base (object_members_base const& x) + : context (), member_ (*this) + { + init (x.build_prefix_, x.build_table_prefix_); } virtual void @@ -63,6 +59,17 @@ protected: context::table_prefix table_prefix_; private: + void + init (bool build_prefix, bool build_table_prefix) + { + build_prefix_ = build_prefix; + build_table_prefix_ = build_table_prefix; + + *this >> names_ >> member_; + *this >> inherits_ >> *this; + } + +private: struct member: traversal::data_member { member (object_members_base& om) @@ -77,7 +84,6 @@ private: object_members_base& om_; }; - context* ctx_; bool build_prefix_; bool build_table_prefix_; @@ -104,11 +110,16 @@ struct object_columns_base: traversal::class_ composite (semantics::data_member&, semantics::class_&); public: - object_columns_base (context& c) - : member_ (c, *this) + object_columns_base () + : member_ (*this) { - *this >> names_ >> member_; - *this >> inherits_ >> *this; + init (); + } + + object_columns_base (object_columns_base const&) + : member_ (*this) + { + init (); } virtual void @@ -121,10 +132,18 @@ public: std::string const& default_name); private: + void + init () + { + *this >> names_ >> member_; + *this >> inherits_ >> *this; + } + +private: struct member: traversal::data_member, context { - member (context& c, object_columns_base& oc) - : context (c), oc_ (oc), first_ (true) + member (object_columns_base& oc) + : oc_ (oc), first_ (true) { } diff --git a/odb/context.cxx b/odb/context.cxx index 6fde943..944756a 100644 --- a/odb/context.cxx +++ b/odb/context.cxx @@ -4,6 +4,7 @@ // license : GNU GPL v3; see accompanying LICENSE file #include // std::toupper, std::is{alpha,upper,lower} +#include #include #include @@ -93,47 +94,85 @@ namespace } context:: +~context () +{ + if (current_ == this) + current_ = 0; +} + +context:: context (ostream& os_, semantics::unit& u, options_type const& ops, data_ptr d) - : data_ (d ? d : data_ptr (new (shared) data)), - os (os_), + : data_ (d ? d : data_ptr (new (shared) data (os_))), + os (data_->os_), unit (u), options (ops), keyword_set (data_->keyword_set_), embedded_schema (ops.generate_schema () && - ops.schema_format ().count (schema_format::embedded)) + ops.schema_format ().count (schema_format::embedded)), + object (data_->object_) { + assert (current_ == 0); + current_ = this; + for (size_t i (0); i < sizeof (keywords) / sizeof (char*); ++i) data_->keyword_set_.insert (keywords[i]); } context:: -context (context& c) +context (const context& c) : data_ (c.data_), os (c.os), unit (c.unit), options (c.options), keyword_set (c.keyword_set), - embedded_schema (c.embedded_schema) + embedded_schema (c.embedded_schema), + object (c.object) { } context:: -context (context& c, ostream& os_) - : data_ (c.data_), - os (os_), - unit (c.unit), - options (c.options), - keyword_set (c.keyword_set), - embedded_schema (c.embedded_schema) +context () + : data_ (current ().data_), + os (current ().os), + unit (current ().unit), + options (current ().options), + keyword_set (current ().keyword_set), + embedded_schema (current ().embedded_schema), + object (current ().object) { } -context:: -~context () +context* context::current_; + +void context:: +diverge (streambuf* sb) +{ + data_->os_stack_.push (data_->os_.rdbuf ()); + data_->os_.rdbuf (sb); +} + +void context:: +restore () { + data_->os_.rdbuf (data_->os_stack_.top ()); + data_->os_stack_.pop (); +} + +semantics::type& context:: +member_type (semantics::data_member& m, string const& key_prefix) +{ + if (key_prefix.empty ()) + return m.type (); + + string const key ("tree-" + key_prefix + "-type"); + + if (m.count (key)) + return *indirect_value (m, key); + + return *indirect_value (m.type (), key); } string context:: @@ -223,11 +262,11 @@ column_name (semantics::data_member& m, string const& p, string const& d) const } string context:: -column_type (semantics::data_member& m, string const& kp) const +column_type (semantics::data_member& m, string const& kp) { return kp.empty () ? m.get ("column-type") - : m.get (kp + "-column-type"); + : indirect_value (m, kp + "-column-type"); } string context::data:: diff --git a/odb/context.hxx b/odb/context.hxx index 365936d..d8c457b 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -8,6 +8,7 @@ #include #include +#include #include #include #include // std::size_t @@ -50,19 +51,16 @@ class context public: typedef std::size_t size_t; typedef std::string string; + typedef std::ostream ostream; + typedef ::options options_type; static string upcase (string const&); public: - static semantics::type& - member_type (semantics::data_member& m, string const& key_prefix) - { - return key_prefix.empty () - ? m.type () - : *m.type ().get ("tree-" + key_prefix + "-type"); - } + semantics::type& + member_type (semantics::data_member& m, string const& key_prefix); // Predicates. // @@ -132,9 +130,8 @@ public: string const& key_prefix, string const& default_name) const; - virtual string - column_type (semantics::data_member&, - string const& key_prefix = string ()) const; + string + column_type (semantics::data_member&, string const& key_prefix = string ()); // Cleaned-up member name that can be used for database names. // @@ -184,31 +181,31 @@ public: public: typedef ::pointer_kind pointer_kind_type; - static pointer_kind_type + pointer_kind_type pointer_kind (semantics::type& p) { return p.get ("pointer-kind"); } - static bool + bool lazy_pointer (semantics::type& p) { return p.get ("pointer-lazy"); } - static bool + bool weak_pointer (semantics::type& p) { return pointer_kind (p) == pk_weak; } - static bool + bool null_pointer (semantics::data_member& m) { return !(m.count ("not-null") || m.type ().count ("not-null")); } - static bool + bool null_pointer (semantics::data_member& m, string const& key_prefix) { if (key_prefix.empty ()) @@ -219,7 +216,7 @@ public: member_type (m, key_prefix).count ("not-null")); } - static semantics::data_member* + semantics::data_member* inverse (semantics::data_member& m) { return object_pointer (m.type ()) @@ -227,7 +224,7 @@ public: : 0; } - static semantics::data_member* + semantics::data_member* inverse (semantics::data_member& m, string const& key_prefix) { if (key_prefix.empty ()) @@ -284,25 +281,55 @@ public: static unsigned short const test_straight_container = 0x10; static unsigned short const test_inverse_container = 0x20; - static bool + bool is_a (semantics::data_member& m, unsigned short flags) { return is_a (m, flags, m.type (), ""); } - static bool + bool is_a (semantics::data_member&, unsigned short flags, semantics::type&, string const& key_prefix); - static bool + bool has_a (semantics::type&, unsigned short flags); + // Diverge output. + // +public: + void + diverge (std::ostream& os) + { + diverge (os.rdbuf ()); + } + + void + diverge (std::streambuf* sb); + + void + restore (); + + // Implementation details. + // private: static bool comp_value_ (semantics::class_&); + template + X + indirect_value (semantics::context const& c, string const& key) + { + typedef X (*func) (context&); + std::type_info const& ti (c.type_info (key)); + + if (ti == typeid (func)) + return c.get (key) (*this); + else + return c.get (key); + } + protected: struct data; typedef cutl::shared_ptr data_ptr; @@ -318,6 +345,8 @@ public: bool embedded_schema; + semantics::class_*& object; // Object currently being traversed. + struct db_type_type { db_type_type () {} @@ -347,7 +376,15 @@ protected: struct data { - virtual ~data () {} + virtual + ~data () {} + data (std::ostream& os): os_ (os.rdbuf ()), object_ (0) {} + + public: + std::ostream os_; + std::stack os_stack_; + + semantics::class_* object_; // Per-database customizable functionality. // @@ -366,15 +403,28 @@ protected: }; public: + virtual + ~context (); + + typedef context root_context; + context (std::ostream&, semantics::unit&, options_type const&, data_ptr = data_ptr ()); - context (context&); - context (context&, std::ostream&); + context (const context&); - virtual - ~context (); + static context& + current () + { + return *current_; + } + +protected: + context (); + +private: + static context* current_; private: context& diff --git a/odb/emitter.cxx b/odb/emitter.cxx index 6cfe64a..2989ffb 100644 --- a/odb/emitter.cxx +++ b/odb/emitter.cxx @@ -3,6 +3,7 @@ // copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file +#include #include using namespace std; @@ -30,7 +31,22 @@ sync () s.resize (n - 1); } + // Temporary restore output diversion. + // + bool r (false); + context& ctx (context::current ()); + + if (ctx.os.rdbuf () == this) + { + ctx.restore (); + r = true; + } + e_.line (s); + + if (r) + ctx.diverge (this); + str (string ()); return 0; } diff --git a/odb/generator.cxx b/odb/generator.cxx index 91548e4..e0187d6 100644 --- a/odb/generator.cxx +++ b/odb/generator.cxx @@ -28,11 +28,9 @@ #include #include -#include -#include -#include -#include -#include +#include + +#include using namespace std; using namespace cutl; @@ -119,7 +117,7 @@ generate (options const& ops, semantics::unit& unit, path const& p) { case database::mysql: { - ctx.reset (new mysql::context (cerr, unit, ops)); + ctx.reset (new relational::mysql::context (cerr, unit, ops)); break; } case database::tracer: @@ -249,7 +247,7 @@ generate (options const& ops, semantics::unit& unit, path const& p) { case database::mysql: { - ctx.reset (new mysql::context (hxx, unit, ops)); + ctx.reset (new relational::mysql::context (hxx, unit, ops)); break; } case database::tracer: @@ -297,7 +295,7 @@ generate (options const& ops, semantics::unit& unit, path const& p) { case database::mysql: { - mysql::generate_header (static_cast (*ctx)); + relational::header::generate (); break; } case database::tracer: @@ -336,7 +334,7 @@ generate (options const& ops, semantics::unit& unit, path const& p) { case database::mysql: { - ctx.reset (new mysql::context (ixx, unit, ops)); + ctx.reset (new relational::mysql::context (ixx, unit, ops)); break; } case database::tracer: @@ -361,7 +359,7 @@ generate (options const& ops, semantics::unit& unit, path const& p) { case database::mysql: { - mysql::generate_inline (static_cast (*ctx)); + relational::inline_::generate (); break; } case database::tracer: @@ -405,8 +403,8 @@ generate (options const& ops, semantics::unit& unit, path const& p) { case database::mysql: { - mysql::context ctx (cxx, unit, ops); - mysql::generate_source (ctx); + relational::mysql::context ctx (cxx, unit, ops); + relational::source::generate (); break; } case database::tracer: @@ -441,8 +439,8 @@ generate (options const& ops, semantics::unit& unit, path const& p) { case database::mysql: { - mysql::context ctx (sql, unit, ops); - mysql::generate_schema (ctx); + relational::mysql::context ctx (sql, unit, ops); + relational::schema::generate (); break; } case database::tracer: diff --git a/odb/makefile b/odb/makefile index ea9385a..fafb406 100644 --- a/odb/makefile +++ b/odb/makefile @@ -30,16 +30,24 @@ tracer/header.cxx \ tracer/inline.cxx \ tracer/source.cxx -# MySQL +# Relational # cxx_ptun += \ -mysql/context.cxx \ -mysql/common.cxx \ -mysql/header.cxx \ -mysql/inline.cxx \ -mysql/source.cxx \ -mysql/schema.cxx \ -mysql/sql-schema.cxx +relational/common.cxx \ +relational/context.cxx \ +relational/header.cxx \ +relational/inline.cxx \ +relational/source.cxx \ +relational/schema.cxx + +# Relational/MySQL +# +cxx_ptun += \ +relational/mysql/common.cxx \ +relational/mysql/context.cxx \ +relational/mysql/header.cxx \ +relational/mysql/source.cxx \ +relational/mysql/schema.cxx cxx_ptun += \ semantics/class.cxx \ diff --git a/odb/mysql/common.cxx b/odb/mysql/common.cxx deleted file mode 100644 index b771990..0000000 --- a/odb/mysql/common.cxx +++ /dev/null @@ -1,566 +0,0 @@ -// file : odb/mysql/common.cxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC -// license : GNU GPL v3; see accompanying LICENSE file - -#include - -#include - -using namespace std; - -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 (context& c) - : member_base (c) - { - } - - member_image_type:: - member_image_type (context& c, - semantics::type& type, - string const& fq_type, - string const& key_prefix) - : member_base (c, "", 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 (context& c) - : member_base (c) - { - } - - member_database_type:: - member_database_type (context& c, - semantics::type& type, - string const& fq_type, - string const& key_prefix) - : member_base (c, "", 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 - // - - query_columns:: - query_columns (context& c) - : object_columns_base (c), - context (c), - ptr_ (true), - decl_ (true), - member_image_type_ (c), - member_database_type_ (c) - { - } - - query_columns:: - query_columns (context& c, semantics::class_& cl) - : object_columns_base (c), - context (c), - ptr_ (true), - decl_ (false), - member_image_type_ (c), - member_database_type_ (c) - { - 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 db_type (member_database_type_.database_type (m)); - - string type ( - "mysql::value_traits< " - + m.type ().fq_name (m.belongs ().hint ()) + ", " - + member_image_type_.image_type (m) + ", " - + db_type - + " >::query_type"); - - if (decl_) - { - os << "// " << name << endl - << "//" << endl - << "static const mysql::query_column<" << endl - << " " << type << "," << endl - << " " << db_type << ">" << endl - << name << ";" - << endl; - } - else - { - string column ("\"`" + table_ + "`.`" + col_name + "`\""); - - os << "const mysql::query_column<" << endl - << " " << type << "," << endl - << " " << db_type << ">" << endl - << scope_ << "::" << name << " (" << endl - << column << ");" - << endl; - } - } - - return true; - } -} diff --git a/odb/mysql/common.hxx b/odb/mysql/common.hxx deleted file mode 100644 index df01e5f..0000000 --- a/odb/mysql/common.hxx +++ /dev/null @@ -1,270 +0,0 @@ -// file : odb/mysql/common.hxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC -// license : GNU GPL v3; see accompanying LICENSE file - -#ifndef ODB_MYSQL_COMMON_HXX -#define ODB_MYSQL_COMMON_HXX - -#include -#include - -namespace mysql -{ - struct member_base: traversal::data_member, context - { - member_base (context& c, string const& var = string ()) - : context (c), var_override_ (var), type_override_ (0) - { - } - - member_base (context& c, - string const& var, - semantics::type& type, - string const& fq_type, - string const& key_prefix) - : context (c), - var_override_ (var), - type_override_ (&type), - fq_type_override_ (fq_type), - key_prefix_ (key_prefix) - { - } - - 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 value types). - 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&) - { - } - - protected: - string var_override_; - semantics::type* type_override_; - string fq_type_override_; - string key_prefix_; - }; - - struct member_image_type: member_base - { - member_image_type (context&); - - member_image_type (context& c, - semantics::type& type, - string const& fq_type, - string const& key_prefix); - - 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 (context&); - - member_database_type (context& c, - semantics::type& type, - string const& fq_type, - string const& key_prefix); - - 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_; - }; - - struct query_columns: object_columns_base, context - { - query_columns (context&); - query_columns (context&, semantics::class_&); - - virtual void - composite (semantics::data_member&, semantics::class_&); - - virtual bool - column (semantics::data_member&, string const&, bool); - - private: - bool ptr_; - bool decl_; - - string scope_; - string table_; - - member_image_type member_image_type_; - member_database_type member_database_type_; - }; -} - -#endif // ODB_MYSQL_COMMON_HXX diff --git a/odb/mysql/context.cxx b/odb/mysql/context.cxx deleted file mode 100644 index b7d6540..0000000 --- a/odb/mysql/context.cxx +++ /dev/null @@ -1,637 +0,0 @@ -// file : odb/mysql/context.cxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC -// license : GNU GPL v3; see accompanying LICENSE file - -#include - -#include -#include - -#include -#include - -using namespace std; - -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) - : base_context (os, u, ops, data_ptr (new (shared) data)), - data_ (static_cast (base_context::data_.get ())) - { - // 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 (context& c) - : base_context (c), - data_ (c.data_) - { - } - - context:: - context (context& c, ostream& os) - : base_context (c, os), - data_ (c.data_) - { - } - - 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 ("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 (context& c, bool& r) - : member_base (c), r_ (r) - { - } - - has_grow_member (context& c, - bool& r, - semantics::type& type, - string const& key_prefix) - : member_base (c, "", type, "", 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 (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 (semantics::class_& c) - { - if (c.count ("mysql::grow")) - return c.get ("mysql::grow"); - - bool r (false); - has_grow ct (r); - has_grow_member mt (*this, r); - traversal::names names; - ct >> names >> mt; - ct.traverse (c); - return r; - } - - bool context:: - grow (semantics::data_member& m) - { - bool r (false); - has_grow_member mt (*this, r); - mt.traverse (m); - return r; - } - - bool context:: - grow (semantics::data_member& m, semantics::type& t, string const& kp) - { - bool r (false); - has_grow_member mt (*this, 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 (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 (context::upcase (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 (context::upcase (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 && - context::upcase (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/mysql/context.hxx b/odb/mysql/context.hxx deleted file mode 100644 index 05b6639..0000000 --- a/odb/mysql/context.hxx +++ /dev/null @@ -1,127 +0,0 @@ -// file : odb/mysql/context.hxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC -// license : GNU GPL v3; see accompanying LICENSE file - -#ifndef ODB_MYSQL_CONTEXT_HXX -#define ODB_MYSQL_CONTEXT_HXX - -#include - -#include - -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 ::context - { - // Predicates. - // - 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: - sql_type const& - db_type (semantics::data_member&, string const& key_prefix = string ()); - - private: - typedef ::context base_context; - - struct data: base_context::data - { - virtual string - column_type_impl (semantics::type&, - string const& type, - semantics::context&, - column_type_flags) const; - }; - - private: - data* data_; - - public: - - public: - context (std::ostream&, semantics::unit&, options_type const&); - context (context&); - context (context&, std::ostream&); - }; -} - -#endif // ODB_MYSQL_CONTEXT_HXX diff --git a/odb/mysql/header.cxx b/odb/mysql/header.cxx deleted file mode 100644 index 59793b0..0000000 --- a/odb/mysql/header.cxx +++ /dev/null @@ -1,1031 +0,0 @@ -// file : odb/mysql/header.cxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC -// license : GNU GPL v3; see accompanying LICENSE file - -#include - -#include -#include - -namespace mysql -{ - namespace - { - struct image_member: member_base - { - image_member (context& c, string const& var = string ()) - : member_base (c, var), member_image_type_ (c) - { - } - - image_member (context& c, - string const& var, - semantics::type& t, - string const& fq_type, - string const& key_prefix) - : member_base (c, var, t, fq_type, key_prefix), - member_image_type_ (c, t, fq_type, 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_; - }; - - struct image_base: traversal::class_, context - { - image_base (context& c): context (c), 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_, context - { - image_type (context& c) - : context (c), member_ (c) - { - *this >> names_member_ >> member_; - } - - virtual void - traverse (type& c) - { - os << "struct image_type"; - - { - image_base b (*this); - traversal::inherits i (b); - inherits (c, i); - } - - os << "{"; - - names (c); - - if (!comp_value (c)) - os << "std::size_t version;"; - - os << "};"; - } - - private: - image_member member_; - traversal::names names_member_; - }; - - // Member-specific traits types for container members. - // - struct container_traits: object_members_base, context - { - container_traits (context& c, semantics::class_& obj) - : object_members_base (c, true, false), context (c) - { - 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 " << - "functions_type;"; - break; - } - case ck_map: - case ck_multimap: - { - os << "typedef map_functions " << - "functions_type;"; - break; - } - case ck_set: - case ck_multiset: - { - os << "typedef set_functions 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; - image_member im (*this, "index_", *it, "index_type", "index"); - im.traverse (m); - } - break; - } - case ck_map: - case ck_multimap: - { - os << "// key" << endl - << "//" << endl; - image_member im (*this, "key_", *kt, "key_type", "key"); - im.traverse (m); - break; - } - case ck_set: - case ck_multiset: - { - os << "// value" << endl - << "//" << endl; - image_member im (*this, "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; - image_member im (*this, "index_", *it, "index_type", "index"); - im.traverse (m); - } - break; - } - case ck_map: - case ck_multimap: - { - os << "// key" << endl - << "//" << endl; - image_member im (*this, "key_", *kt, "key_type", "key"); - im.traverse (m); - break; - } - case ck_set: - case ck_multiset: - { - break; - } - } - - os << "// value" << endl - << "//" << endl; - image_member im (*this, "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_, context - { - class_ (context& c) - : context (c), image_type_ (c), id_image_member_ (c, "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 ("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" - << "{"; - - { - query_columns t (*this); - 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. - // - { - container_traits t (*this, 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" << endl - << "static result" << 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: - image_type image_type_; - image_member id_image_member_; - }; - } - - void - generate_header (context& ctx) - { - traversal::unit unit; - traversal::defines unit_defines; - traversal::namespace_ ns; - class_ c (ctx); - - unit >> unit_defines >> ns; - unit_defines >> c; - - traversal::defines ns_defines; - - ns >> ns_defines >> ns; - ns_defines >> c; - - ctx.os << "#include " << endl - << "#include " << endl - << "#include " << endl; - - if (ctx.options.generate_query ()) - ctx.os << "#include " << endl; - - ctx.os << endl - << "#include " << endl - << endl; - - ctx.os << "namespace odb" - << "{"; - - unit.dispatch (ctx.unit); - - ctx.os << "}"; - } -} diff --git a/odb/mysql/header.hxx b/odb/mysql/header.hxx deleted file mode 100644 index ec7cbf1..0000000 --- a/odb/mysql/header.hxx +++ /dev/null @@ -1,17 +0,0 @@ -// file : odb/mysql/header.hxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC -// license : GNU GPL v3; see accompanying LICENSE file - -#ifndef ODB_MYSQL_HEADER_HXX -#define ODB_MYSQL_HEADER_HXX - -#include - -namespace mysql -{ - void - generate_header (context&); -} - -#endif // ODB_MYSQL_HEADER_HXX diff --git a/odb/mysql/inline.cxx b/odb/mysql/inline.cxx deleted file mode 100644 index 1ab16ca..0000000 --- a/odb/mysql/inline.cxx +++ /dev/null @@ -1,129 +0,0 @@ -// file : odb/mysql/inline.cxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC -// license : GNU GPL v3; see accompanying LICENSE file - -#include -#include - -namespace mysql -{ - namespace - { - struct class_: traversal::class_, context - { - class_ (context& c) - : context (c) - { - } - - 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; - - */ - } - }; - } - - void - generate_inline (context& ctx) - { - traversal::unit unit; - traversal::defines unit_defines; - traversal::namespace_ ns; - class_ c (ctx); - - unit >> unit_defines >> ns; - unit_defines >> c; - - traversal::defines ns_defines; - - ns >> ns_defines >> ns; - ns_defines >> c; - - ctx.os << "namespace odb" - << "{"; - - unit.dispatch (ctx.unit); - - ctx.os << "}"; - } -} diff --git a/odb/mysql/inline.hxx b/odb/mysql/inline.hxx deleted file mode 100644 index e902ef3..0000000 --- a/odb/mysql/inline.hxx +++ /dev/null @@ -1,17 +0,0 @@ -// file : odb/mysql/inline.hxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC -// license : GNU GPL v3; see accompanying LICENSE file - -#ifndef ODB_MYSQL_INLINE_HXX -#define ODB_MYSQL_INLINE_HXX - -#include - -namespace mysql -{ - void - generate_inline (context&); -} - -#endif // ODB_MYSQL_INLINE_HXX diff --git a/odb/mysql/schema.cxx b/odb/mysql/schema.cxx deleted file mode 100644 index b40a019..0000000 --- a/odb/mysql/schema.cxx +++ /dev/null @@ -1,11 +0,0 @@ -// file : odb/mysql/schema.cxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC -// license : GNU GPL v3; see accompanying LICENSE file - -#include - -namespace mysql -{ - -} diff --git a/odb/mysql/schema.hxx b/odb/mysql/schema.hxx deleted file mode 100644 index 1defad7..0000000 --- a/odb/mysql/schema.hxx +++ /dev/null @@ -1,325 +0,0 @@ -// file : odb/mysql/schema.hxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC -// license : GNU GPL v3; see accompanying LICENSE file - -#ifndef ODB_MYSQL_SCHEMA_HXX -#define ODB_MYSQL_SCHEMA_HXX - -#include - -#include -#include - -namespace mysql -{ - struct schema_context: context - { - typedef std::set tables; - - schema_context (context& c, std::ostream& os, emitter& e) - : context (c, os), e_ (e) - { - } - - schema_context (schema_context& c) : context (c), e_ (c.e_) {} - - emitter& e_; - }; - - // - // Drop. - // - - struct member_drop: object_members_base, schema_context - { - member_drop (schema_context& c, tables& t) - : object_members_base (c, false, true), - schema_context (c), - 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; - - e_.pre (); - os << "DROP TABLE IF EXISTS `" << name << "`" << endl; - e_.post (); - - tables_.insert (name); - } - - private: - tables& tables_; - }; - - struct class_drop: traversal::class_, schema_context - { - class_drop (context& c, emitter& e) - : schema_context (c, os, e), os (e), - member_drop_ (*this, 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; - - e_.pre (); - os << "DROP TABLE IF EXISTS `" << name << "`" << endl; - e_.post (); - - tables_.insert (name); - - // Drop tables for members. - // - member_drop_.traverse (c); - } - - private: - tables tables_; - emitter_ostream os; - member_drop member_drop_; - }; - - // - // Create. - // - - struct object_columns: object_columns_base, schema_context - { - object_columns (schema_context& c, string const& prefix = string ()) - : object_columns_base (c), schema_context (c), 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 << " `" << name << "` " << column_type (m, prefix_); - - if (m.count ("id")) - os << " PRIMARY KEY"; - - using semantics::class_; - if (class_* c = object_pointer (member_type (m, prefix_))) - { - os << " REFERENCES `" << table_name (*c) << "` (`" << - column_name (id_member (*c)) << "`)"; - } - - return true; - } - - private: - string prefix_; - }; - - struct member_create: object_members_base, schema_context - { - member_create (schema_context& c, semantics::class_& object, tables& t) - : object_members_base (c, false, true), - schema_context (c), - id_member_ (id_member (object)), - 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; - - e_.pre (); - os << "CREATE TABLE `" << name << "` (" << endl; - - // object_id (simple value) - // - string id_name (column_name (m, "id", "object_id")); - os << " `" << id_name << "` " << column_type (id_member_, "ref"); - - // index (simple value) - // - string index_name; - bool ordered (ck == ck_ordered && !unordered (m)); - if (ordered) - { - index_name = column_name (m, "index", "index"); - - os << "," << endl - << " `" << index_name << "` " << column_type (m, "index"); - } - - // 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)) - { - object_columns oc (*this); - oc.traverse_composite (m, *ckt, "key", "key"); - } - else - { - object_columns oc (*this, "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)) - { - object_columns oc (*this); - oc.traverse_composite (m, *cvt, "value", "value"); - } - else - { - object_columns oc (*this, "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 << "`)"; - - os << ")"; - - string const& engine (options.mysql_engine ()); - - if (engine != "default") - os << endl - << " ENGINE=" << engine; - - os << endl; - e_.post (); - - tables_.insert (name); - } - - private: - semantics::data_member& id_member_; - tables& tables_; - }; - - struct class_create: traversal::class_, schema_context - { - class_create (context& c, emitter& e) - : schema_context (c, os, e), os (e) - { - } - - 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; - - e_.pre (); - os << "CREATE TABLE `" << name << "` (" << endl; - - { - object_columns oc (*this); - oc.traverse (c); - } - - os << ")"; - - string const& engine (options.mysql_engine ()); - - if (engine != "default") - os << endl - << " ENGINE=" << engine; - - os << endl; - e_.post (); - - tables_.insert (name); - - // Create tables for members. - // - { - member_create mc (*this, c, tables_); - mc.traverse (c); - } - } - - private: - tables tables_; - emitter_ostream os; - }; -} - -#endif // ODB_MYSQL_SCHEMA_HXX diff --git a/odb/mysql/source.cxx b/odb/mysql/source.cxx deleted file mode 100644 index bc7340a..0000000 --- a/odb/mysql/source.cxx +++ /dev/null @@ -1,3266 +0,0 @@ -// file : odb/mysql/source.cxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC -// license : GNU GPL v3; see accompanying LICENSE file - -#include - -#include -#include -#include -#include - -#include -#include -#include - -using namespace std; - -namespace mysql -{ - namespace - { - struct schema_emitter: emitter, context - { - schema_emitter (context& c): context (c) {} - - 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, context - { - object_columns (context& c, - std::string const& table_name, - bool out, - char const* suffix = "") - : object_columns_base (c), - context (c), - table_name_ (table_name), - out_ (out), - first_ (true), - suffix_ (suffix) - { - } - - object_columns (context& c, - std::string const& table_name, - bool out, - bool first, - char const* suffix = "") - : object_columns_base (c), - context (c), - table_name_ (table_name), - out_ (out), - first_ (first), - suffix_ (suffix) - { - } - - virtual 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, context - { - object_joins (context& c, semantics::class_& scope, bool query) - : object_columns_base (c), - context (c), - query_ (query), - table_ (table_name (scope)), - id_ (id_member (scope)) - { - } - - size_t - count () const - { - return joins_.size (); - } - - void - write () - { - for (joins::iterator i (joins_.begin ()); i != joins_.end (); ++i) - { - if (i->table.empty ()) - continue; - - os << "\" LEFT JOIN `" << i->table << "` ON "; - - for (conditions::iterator b (i->cond.begin ()), j (b); - j != i->cond.end (); ++j) - { - if (j != b) - os << " OR "; - - os << *j; - } - - os << "\"" << endl; - } - } - - virtual bool - column (semantics::data_member& m, string const& col_name, bool) - { - semantics::class_* c (object_pointer (m.type ())); - - if (c == 0) - return true; - - string t, dt; - ostringstream cond, dcond; - - 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 set conditions; - - struct join - { - string table; - conditions cond; - - void - swap (join& o) - { - table.swap (o.table); - cond.swap (o.cond); - } - }; - - typedef vector joins; - typedef map table_map; - - joins joins_; - table_map table_map_; - }; - - const char* integer_buffer_types[] = - { - "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: member_base - { - bind_member (context& c, - string const& var = string (), - string const& arg = string ()) - : member_base (c, var), arg_override_ (arg) - { - } - - bind_member (context& c, - string const& var, - string const& arg, - semantics::type& t, - string const& fq_type, - string const& key_prefix) - : member_base (c, var, t, fq_type, key_prefix), arg_override_ (arg) - { - } - - 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 (" << 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 (" << 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 (" << 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 (" << 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 (" << 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 (" << endl - << "" << arg << "." << mi.var << "value.capacity ());" - << b << ".length = &" << arg << "." << mi.var << "size;" - << b << ".is_null = &" << arg << "." << mi.var << "null;"; - } - - private: - string b; - string arg; - string arg_override_; - }; - - struct bind_base: traversal::class_, context - { - bind_base (context& c) - : context (c) - { - } - - 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: member_base - { - grow_member (context& c, size_t& index) - : member_base (c), index_ (index) - { - } - - grow_member (context& c, - size_t& index, - string const& var, - semantics::type& t, - string const& fq_type, - string const& key_prefix) - : member_base (c, var, t, fq_type, key_prefix), index_ (index) - { - } - - 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; - size_t& index_; - }; - - struct grow_base: traversal::class_, context - { - grow_base (context& c, size_t& index) - : context (c), 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); - } - - private: - size_t& index_; - }; - - // - // init image - // - - struct init_image_member: member_base - { - init_image_member (context& c, - string const& var = string (), - string const& member = string ()) - : member_base (c, var), - member_image_type_ (c), - member_database_type_ (c), - member_override_ (member) - { - } - - init_image_member (context& c, - string const& var, - string const& member, - semantics::type& t, - string const& fq_type, - string const& key_prefix) - : member_base (c, var, t, fq_type, key_prefix), - member_image_type_ (c, t, fq_type, key_prefix), - member_database_type_ (c, t, fq_type, key_prefix), - member_override_ (member) - { - } - - 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 (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 (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 (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 (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 (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 (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_; - - string member_override_; - }; - - struct init_image_base: traversal::class_, context - { - init_image_base (context& c) - : context (c) - { - } - - 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: member_base - { - init_value_member (context& c, string const& member = string ()) - : member_base (c), - member_image_type_ (c), - member_database_type_ (c), - member_override_ (member) - { - } - - init_value_member (context& c, - string const& var, - string const& member, - semantics::type& t, - string const& fq_type, - string const& key_prefix) - : member_base (c, var, t, fq_type, key_prefix), - member_image_type_ (c, t, fq_type, key_prefix), - member_database_type_ (c, t, fq_type, key_prefix), - member_override_ (member) - { - } - - 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_; - - string member_override_; - }; - - struct init_value_base: traversal::class_, context - { - init_value_base (context& c) - : context (c) - { - } - - 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, context - { - container_traits (context& c, semantics::class_& obj) - : object_members_base (c, true, true), - context (c), - 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)) - { - object_columns t (*this, 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)) - { - object_columns t (*this, 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)) - { - object_columns t (*this, 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)) - { - object_columns t (*this, 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 ("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() - // - { - bind_member bind_id (*this, "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; - bind_member bm ( - *this, "index_", "c", *it, "index_type", "index"); - bm.traverse (m); - } - break; - } - case ck_map: - case ck_multimap: - { - os << "// key" << endl - << "//" << endl; - bind_member bm (*this, "key_", "c", *kt, "key_type", "key"); - bm.traverse (m); - break; - } - case ck_set: - case ck_multiset: - { - os << "// value" << endl - << "//" << endl; - bind_member bm (*this, "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; - bind_member bm ( - *this, "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; - bind_member bm (*this, "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; - bind_member bm (*this, "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; - grow_member gm ( - *this, index, "index_", *it, "index_type", "index"); - gm.traverse (m); - } - break; - } - case ck_map: - case ck_multimap: - { - os << "// key" << endl - << "//" << endl; - grow_member gm (*this, index, "key_", *kt, "key_type", "key"); - gm.traverse (m); - break; - } - case ck_set: - case ck_multiset: - { - break; - } - } - - os << "// value" << endl - << "//" << endl; - grow_member gm (*this, 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; - - init_image_member im ( - *this, "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; - - init_image_member im ( - *this, "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; - { - init_image_member im ( - *this, "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; - - init_value_member im ( - *this, "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; - - init_value_member im (*this, "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. - // - init_value_member im ( - *this, "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);" - << "}"; - } - } - - private: - string obj_scope_; - semantics::class_& object_; - semantics::data_member& id_member_; - }; - - // Container statement cache members. - // - struct container_cache_members: object_members_base, context - { - container_cache_members (context& c) - : object_members_base (c, true, false), context (c) - { - } - - 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, context - { - container_cache_init_members (context& c) - : object_members_base (c, true, false), context (c), first_ (true) - { - } - - virtual void - container (semantics::data_member& m) - { - if (first_) - { - os << endl - << ": "; - first_ = false; - } - else - os << "," << endl - << " "; - - os << prefix_ << m.name () << " (c)"; - } - - private: - bool first_; - }; - - // Calls for container members. - // - struct container_calls: object_members_base, context - { - enum call_type - { - persist_call, - load_call, - update_call, - erase_call - }; - - container_calls (context& c, call_type call) - : object_members_base (c, true, false), context (c), 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; - } - } - } - - private: - call_type call_; - string obj_prefix_; - }; - - // - // - struct class_: traversal::class_, context - { - class_ (context& c) - : context (c), - grow_base_ (c, index_), - grow_member_ (c, index_), - bind_base_ (c), - bind_member_ (c), - bind_id_member_ (c, "id_"), - init_image_base_ (c), - init_image_member_ (c), - init_id_image_member_ (c, "id_", "id"), - init_value_base_ (c), - init_value_member_ (c), - init_id_value_member_ (c, "id"), - - schema_emitter_ (c), - schema_drop_ (c, schema_emitter_), - schema_create_ (c, schema_emitter_) - { - 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" - << "{"; - - container_cache_members cm (*this); - cm.traverse (c); - - os << (containers ? "\n" : "") - << "container_statement_cache_type (mysql::connection&" << - (containers ? " c" : "") << ")"; - - container_cache_init_members im (*this); - im.traverse (c); - - os << "{" - << "}" - << "};"; - } - - // Traits types. - // - if (containers) - { - container_traits t (*this, c); - t.traverse (c); - } - - // query columns - // - if (options.generate_query ()) - { - query_columns t (*this, c); - t.traverse (c); - } - - string const& table (table_name (c)); - - // persist_statement - // - os << "const char* const " << traits << "::persist_statement =" << endl - << "\"INSERT INTO `" << table << "` (\"" << endl; - - { - object_columns t (*this, 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; - - { - object_columns t (*this, table, true); - t.traverse (c); - } - - os << "\"" << endl - << "\" FROM `" << table << "`\"" << endl; - - { - object_joins t (*this, c, false); - 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; - - { - object_columns t (*this, 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 ()) - { - object_joins oj (*this, c, true); - oj.traverse (c); - - // We only need DISTINCT if there are joins (object pointers) - // and can optimize it out otherwise. - // - os << "const char* const " << traits << "::query_clause =" << endl - << "\"SELECT " << (oj.count () ? "DISTINCT " : "") << "\"" << endl; - - { - object_columns oc (*this, 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 ());" - << "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 (st.id ());" - << endl; - - if (straight_containers) - { - // Initialize id_image. - // - os << "id_image_type& i (sts.id_image ());" - << "init (i, obj." << id.name () << ");" - << endl; - - container_calls t (*this, 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 ());" - << 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; - container_calls t (*this, 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 ());" - << 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; - container_calls t (*this, 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_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_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; - container_calls t (*this, 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 ());" - << "details::shared_ptr st;" - << endl - << "query_ (db, q, sts, st);" - << endl - << "details::shared_ptr > r (" << endl - << "new (details::shared) mysql::result_impl (st, sts));" - << "return result (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 ());" - << "details::shared_ptr st;" - << endl - << "query_ (db, q, sts, st);" - << endl - << "details::shared_ptr > r (" << endl - << "new (details::shared) mysql::result_impl (st, sts));" - << "return result (r);" - << "}"; - - os << "void " << traits << "::" << endl - << "query_ (database&," << endl - << "const query_type& q," << endl - << "mysql::object_statements< object_type >& sts," - << "details::shared_ptr& 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_; - - grow_base grow_base_; - traversal::inherits grow_base_inherits_; - grow_member grow_member_; - traversal::names grow_member_names_; - - bind_base bind_base_; - traversal::inherits bind_base_inherits_; - bind_member bind_member_; - traversal::names bind_member_names_; - bind_member bind_id_member_; - - init_image_base init_image_base_; - traversal::inherits init_image_base_inherits_; - init_image_member init_image_member_; - traversal::names init_image_member_names_; - - init_image_member init_id_image_member_; - - init_value_base init_value_base_; - traversal::inherits init_value_base_inherits_; - init_value_member init_value_member_; - traversal::names init_value_member_names_; - init_value_member init_id_value_member_; - - schema_emitter schema_emitter_; - class_drop schema_drop_; - class_create schema_create_; - }; - } - - void - generate_source (context& ctx) - { - traversal::unit unit; - traversal::defines unit_defines; - traversal::namespace_ ns; - class_ c (ctx); - - unit >> unit_defines >> ns; - unit_defines >> c; - - traversal::defines ns_defines; - - ns >> ns_defines >> ns; - ns_defines >> c; - - // - // - ctx.os << "#include " << endl; - - if (ctx.embedded_schema) - ctx.os << "#include " << endl; - - ctx.os << endl; - - // - // - ctx.os << "#include " << endl - << "#include " << endl - << "#include " << endl - << "#include " << endl - << "#include " << endl - << "#include " << endl - << "#include " << endl - << "#include " << endl - << "#include " << endl - << "#include " << endl; - - if (ctx.options.generate_query ()) - ctx.os << "#include " << endl; - - ctx.os << endl; - - // Details includes. - // - ctx.os << "#include " << endl; - - if (ctx.options.generate_query ()) - ctx.os << "#include " << endl; - - ctx.os << endl; - - ctx.os << "namespace odb" - << "{"; - - unit.dispatch (ctx.unit); - - ctx.os << "}"; - } -} diff --git a/odb/mysql/source.hxx b/odb/mysql/source.hxx deleted file mode 100644 index 43a051b..0000000 --- a/odb/mysql/source.hxx +++ /dev/null @@ -1,17 +0,0 @@ -// file : odb/mysql/source.hxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC -// license : GNU GPL v3; see accompanying LICENSE file - -#ifndef ODB_MYSQL_SOURCE_HXX -#define ODB_MYSQL_SOURCE_HXX - -#include - -namespace mysql -{ - void - generate_source (context&); -} - -#endif // ODB_MYSQL_SOURCE_HXX diff --git a/odb/mysql/sql-schema.cxx b/odb/mysql/sql-schema.cxx deleted file mode 100644 index 7c71b24..0000000 --- a/odb/mysql/sql-schema.cxx +++ /dev/null @@ -1,98 +0,0 @@ -// file : odb/mysql/sql-schema.cxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC -// license : GNU GPL v3; see accompanying LICENSE file - -#include -#include -#include - -using namespace std; - -namespace mysql -{ - namespace - { - struct schema_emitter: emitter, context - { - schema_emitter (context& c): context (c) {} - - 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_schema (context& ctx) - { - ctx.os << file_header; - schema_emitter emitter (ctx); - - // Drop. - // - { - traversal::unit unit; - traversal::defines unit_defines; - traversal::namespace_ ns; - class_drop c (ctx, emitter); - - unit >> unit_defines >> ns; - unit_defines >> c; - - traversal::defines ns_defines; - - ns >> ns_defines >> ns; - ns_defines >> c; - unit.dispatch (ctx.unit); - } - - ctx.os << endl; - - // Create. - // - { - traversal::unit unit; - traversal::defines unit_defines; - traversal::namespace_ ns; - class_create c (ctx, 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/mysql/sql-schema.hxx b/odb/mysql/sql-schema.hxx deleted file mode 100644 index fdbdf28..0000000 --- a/odb/mysql/sql-schema.hxx +++ /dev/null @@ -1,17 +0,0 @@ -// file : odb/mysql/sql-schema.hxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC -// license : GNU GPL v3; see accompanying LICENSE file - -#ifndef ODB_MYSQL_SQL_SCHEMA_HXX -#define ODB_MYSQL_SQL_SCHEMA_HXX - -#include - -namespace mysql -{ - void - generate_schema (context&); -} - -#endif // ODB_MYSQL_SQL_SCHEMA_HXX 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 +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include +#include // std::size_ +#include // abort +#include +#include // abi::__cxa_demangle + +#include + +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 +// 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 +#include // std::size_t +#include +#include + +#include +#include + +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 + 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 + friend struct entry; + + static void + init () + { + if (factory::count_++ == 0) + factory::map_ = new typename factory::map; + } + + static void + term () + { + if (--factory::count_ == 0) + delete factory::map_; + } + + typedef B* (*create_func) (B const&); + typedef std::map map; + static map* map_; + static std::size_t count_; + }; + + template + typename factory::map* factory::map_; + + template + std::size_t factory::count_; + + struct entry_base + { + static database + db (std::type_info const&); + }; + + template + struct entry: entry_base + { + typedef typename D::base base; + + entry () + { + factory::init (); + (*factory::map_)[db (typeid (D))] = &create; + } + + ~entry () + { + factory::term (); + } + + static base* + create (base const& prototype) + { + return new D (prototype); + } + }; + + template + struct instance + { + typedef relational::factory factory; + + ~instance () + { + delete x_; + } + + instance () + { + B prototype; + x_ = factory::create (prototype); + } + + template + instance (A1& a1) + { + B prototype (a1); + x_ = factory::create (prototype); + } + + template + instance (A1 const& a1) + { + B prototype (a1); + x_ = factory::create (prototype); + } + + template + instance (A1& a1, A2& a2) + { + B prototype (a1, a2); + x_ = factory::create (prototype); + } + + template + instance (A1 const& a1, A2 const& a2) + { + B prototype (a1, a2); + x_ = factory::create (prototype); + } + + template + instance (A1& a1, A2& a2, A3& a3) + { + B prototype (a1, a2, a3); + x_ = factory::create (prototype); + } + + template + instance (A1 const& a1, A2 const& a2, A3 const& a3) + { + B prototype (a1, a2, a3); + x_ = factory::create (prototype); + } + + template + instance (A1& a1, A2& a2, A3& a3, A4& a4) + { + B prototype (a1, a2, a3, a4); + x_ = factory::create (prototype); + } + + template + instance (A1 const& a1, A2 const& a2, A3 const& a3, A4 const& a4) + { + B prototype (a1, a2, a3, a4); + x_ = factory::create (prototype); + } + + template + instance (A1& a1, A2& a2, A3& a3, A4& a4, A5& a5) + { + B prototype (a1, a2, a3, a4, a5); + x_ = factory::create (prototype); + } + + template + 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 + inline traversal::edge_base& + operator>> (instance& n, traversal::edge_base& e) + { + n->edge_traverser (e); + return e; + } + + template + inline traversal::node_base& + operator>> (traversal::edge_base& e, instance& 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 +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +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 +// 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 + +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 (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 + +#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 +// 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 +// 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 +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include +#include + +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 c; + + unit >> unit_defines >> ns; + unit_defines >> c; + + traversal::defines ns_defines; + + ns >> ns_defines >> ns; + ns_defines >> c; + + os << "#include " << endl + << "#include " << endl + << "#include " << endl; + + if (ops.generate_query ()) + os << "#include " << endl; + + os << endl + << "#include " << 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 +// 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 //@@ ?? + +#include +#include + +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 b; + traversal::inherits i (*b); + inherits (c, i); + } + + os << "{"; + + names (c); + + if (!comp_value (c)) + os << "std::size_t version;"; + + os << "};"; + } + + private: + instance 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 " << + "functions_type;"; + break; + } + case ck_map: + case ck_multimap: + { + os << "typedef map_functions " << + "functions_type;"; + break; + } + case ck_set: + case ck_multiset: + { + os << "typedef set_functions 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 im ("index_", *it, "index_type", "index"); + im->traverse (m); + } + break; + } + case ck_map: + case ck_multimap: + { + os << "// key" << endl + << "//" << endl; + instance im ("key_", *kt, "key_type", "key"); + im->traverse (m); + break; + } + case ck_set: + case ck_multiset: + { + os << "// value" << endl + << "//" << endl; + instance 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 im ("index_", *it, "index_type", "index"); + im->traverse (m); + } + break; + } + case ck_map: + case ck_multimap: + { + os << "// key" << endl + << "//" << endl; + instance im ("key_", *kt, "key_type", "key"); + im->traverse (m); + break; + } + case ck_set: + case ck_multiset: + { + break; + } + } + + os << "// value" << endl + << "//" << endl; + instance 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 ("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 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 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" << endl + << "static result" << 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_; + instance 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 +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include +#include + +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 +// 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 +#include + +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 +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include + +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_; + } +} 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 +// 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 +#include + +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 +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include +#include + +#include +#include + +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 (root_context::data_.get ())), + data_ (static_cast (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 ("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 (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 ("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 (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 +// 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 + +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 (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 +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include +#include + +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_; + } + } +} 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 +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include +#include + +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_; + + struct class_create: relational::class_create, create_common + { + class_create (base const& x): base (x) {} + }; + entry 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 +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include +#include + +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 (" << 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 (" << 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 (" << 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 (" << 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 (" << 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 (" << 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_; + + // + // 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_; + + // + // 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 (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 (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 (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 (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 (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 (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 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_; + } + } +} 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 +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include +#include + +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 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 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 +// 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 +#include + +#include + +#include +#include + +namespace relational +{ + namespace schema + { + typedef std::set 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_; + }; + + // + // 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 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 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 oc; + oc->traverse_composite (m, *ckt, "key", "key"); + } + else + { + instance 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 oc; + oc->traverse_composite (m, *cvt, "value", "value"); + } + else + { + instance 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 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_; + }; + } +} + +#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 +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include +#include + +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 " << endl; + + if (ctx.embedded_schema) + os << "#include " << endl; + + os << endl; + + // + // + os << "#include " << endl + << "#include " << endl + << "#include " << endl + << "#include " << endl + << "#include " << endl + << "#include " << endl + << "#include " << endl + << "#include " << endl + << "#include " << endl + << "#include " << endl; + + if (ops.generate_query ()) + os << "#include " << endl; + + os << endl; + + // Details includes. + // + os << "#include " << endl; + + if (ops.generate_query ()) + os << "#include " << 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 +// 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 // @@ ?? + +#include +#include +#include +#include + +#include + +#include +#include +#include + +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 conditions; + + struct join + { + string table; + conditions cond; + + void + swap (join& o) + { + table.swap (o.table); + cond.swap (o.cond); + } + }; + + typedef std::vector joins; + typedef std::map 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 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 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 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 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 ("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_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 bm ( + "index_", "c", *it, "index_type", "index"); + bm->traverse (m); + } + break; + } + case ck_map: + case ck_multimap: + { + os << "// key" << endl + << "//" << endl; + instance bm ("key_", "c", *kt, "key_type", "key"); + bm->traverse (m); + break; + } + case ck_set: + case ck_multiset: + { + os << "// value" << endl + << "//" << endl; + instance 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 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 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 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 gm ( + index, "index_", *it, "index_type", "index"); + gm->traverse (m); + } + break; + } + case ck_map: + case ck_multimap: + { + os << "// key" << endl + << "//" << endl; + instance gm (index, "key_", *kt, "key_type", "key"); + gm->traverse (m); + break; + } + case ck_set: + case ck_multiset: + { + break; + } + } + + os << "// value" << endl + << "//" << endl; + instance 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 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 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 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 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 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 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 cm; + cm->traverse (c); + + os << (containers ? "\n" : "") + << "container_statement_cache_type (mysql::connection&" << + (containers ? " c" : "") << ")"; + + instance im; + im->traverse (c); + + os << "{" + << "}" + << "};"; + } + + // Traits types. + // + if (containers) + { + instance t (c); + t->traverse (c); + } + + // query columns + // + if (options.generate_query ()) + { + instance 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 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 t (table, true); + t->traverse (c); + } + + os << "\"" << endl + << "\" FROM `" << table << "`\"" << endl; + + { + bool f (false); + instance 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 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 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 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 ());" + << "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 (st.id ());" + << endl; + + if (straight_containers) + { + // Initialize id_image. + // + os << "id_image_type& i (sts.id_image ());" + << "init (i, obj." << id.name () << ");" + << endl; + + instance 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 ());" + << 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 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 ());" + << 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 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_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_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 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 ());" + << "details::shared_ptr st;" + << endl + << "query_ (db, q, sts, st);" + << endl + << "details::shared_ptr > r (" << endl + << "new (details::shared) mysql::result_impl (st, sts));" + << "return result (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 ());" + << "details::shared_ptr st;" + << endl + << "query_ (db, q, sts, st);" + << endl + << "details::shared_ptr > r (" << endl + << "new (details::shared) mysql::result_impl (st, sts));" + << "return result (r);" + << "}"; + + os << "void " << traits << "::" << endl + << "query_ (database&," << endl + << "const query_type& q," << endl + << "mysql::object_statements< object_type >& sts," + << "details::shared_ptr& 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_; + traversal::inherits grow_base_inherits_; + instance grow_member_; + traversal::names grow_member_names_; + + instance bind_base_; + traversal::inherits bind_base_inherits_; + instance bind_member_; + traversal::names bind_member_names_; + instance bind_id_member_; + + instance init_image_base_; + traversal::inherits init_image_base_inherits_; + instance init_image_member_; + traversal::names init_image_member_names_; + + instance init_id_image_member_; + + instance init_value_base_; + traversal::inherits init_value_base_inherits_; + instance init_value_member_; + traversal::names init_value_member_names_; + instance init_id_value_member_; + + schema_emitter schema_emitter_; + instance schema_drop_; + instance schema_create_; + }; + } +} + +#endif // ODB_RELATIONAL_SOURCE_HXX diff --git a/odb/type-processor.cxx b/odb/type-processor.cxx index 190b102..af6ab3d 100644 --- a/odb/type-processor.cxx +++ b/odb/type-processor.cxx @@ -8,8 +8,26 @@ #include #include +using namespace std; + namespace { + // Indirect (dynamic) context values. + // + static semantics::type* + id_tree_type (context& c) + { + semantics::data_member& id (c.id_member (*c.object)); + return &id.type (); + } + + static string + id_column_type (context& c) + { + semantics::data_member& id (c.id_member (*c.object)); + return id.get ("ref-column-type"); + } + struct data_member: traversal::data_member, context { data_member (context& c) @@ -408,6 +426,9 @@ namespace // Process member data. // + m.set ("tree-id-type", &id_tree_type); + m.set ("id-column-type", &id_column_type); + process_container_value (*vt, m, "value", true); if (it != 0) @@ -417,7 +438,7 @@ namespace process_container_value (*kt, m, "key", false); // If this is an inverse side of a bidirectional object relationship - // and it is an ordred container, mark it as unordred since there is + // and it is an ordered container, mark it as unordred since there is // no concept of order in this construct. // if (ck == ck_ordered && m.count ("value-inverse")) -- cgit v1.1