From add8086259fb21f1e42aba6546c55e607d87ce93 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 11 Mar 2011 14:24:25 +0200 Subject: Infrastructure setup and schema generation for SQLite --- odb/generator.cxx | 90 ++-- odb/makefile | 9 + odb/odb.cxx | 1 + odb/option-functions.cxx | 5 + odb/option-types.cxx | 1 + odb/option-types.hxx | 1 + odb/options.cli | 17 +- odb/relational/sqlite/common.cxx | 448 +++++++++++++++++++ odb/relational/sqlite/common.hxx | 226 ++++++++++ odb/relational/sqlite/context.cxx | 632 +++++++++++++++++++++++++++ odb/relational/sqlite/context.hxx | 120 +++++ odb/relational/sqlite/header.cxx | 166 +++++++ odb/relational/sqlite/schema.cxx | 44 ++ odb/relational/sqlite/source.cxx | 899 ++++++++++++++++++++++++++++++++++++++ 14 files changed, 2607 insertions(+), 52 deletions(-) create mode 100644 odb/relational/sqlite/common.cxx create mode 100644 odb/relational/sqlite/common.hxx create mode 100644 odb/relational/sqlite/context.cxx create mode 100644 odb/relational/sqlite/context.hxx create mode 100644 odb/relational/sqlite/header.cxx create mode 100644 odb/relational/sqlite/schema.cxx create mode 100644 odb/relational/sqlite/source.cxx diff --git a/odb/generator.cxx b/odb/generator.cxx index a6431ed..60f8e2a 100644 --- a/odb/generator.cxx +++ b/odb/generator.cxx @@ -24,6 +24,7 @@ #include #include +#include using namespace std; using namespace cutl; @@ -96,6 +97,33 @@ generator () { } +static auto_ptr +create_context (ostream& os, semantics::unit& unit, options const& ops) +{ + auto_ptr r; + + switch (ops.database ()) + { + case database::mysql: + { + r.reset (new relational::mysql::context (os, unit, ops)); + break; + } + case database::sqlite: + { + r.reset (new relational::sqlite::context (os, unit, ops)); + break; + } + case database::tracer: + { + r.reset (new context (os, unit, ops)); + break; + } + } + + return r; +} + void generator:: generate (options const& ops, semantics::unit& unit, path const& p) { @@ -103,24 +131,10 @@ generate (options const& ops, semantics::unit& unit, path const& p) { // Process types. // + if (ops.database () != database::tracer) { - auto_ptr ctx; - - switch (ops.database ()) - { - case database::mysql: - { - ctx.reset (new relational::mysql::context (cerr, unit, ops)); - break; - } - case database::tracer: - { - break; - } - } - - if (ctx.get () != 0) - relational::process_types (); + auto_ptr ctx (create_context (cerr, unit, ops)); + relational::process_types (); } // Output files. @@ -232,21 +246,7 @@ generate (options const& ops, semantics::unit& unit, path const& p) // { cxx_filter filt (hxx); - auto_ptr ctx; - - switch (ops.database ()) - { - case database::mysql: - { - ctx.reset (new relational::mysql::context (hxx, unit, ops)); - break; - } - case database::tracer: - { - ctx.reset (new context (hxx, unit, ops)); - break; - } - } + auto_ptr ctx (create_context (hxx, unit, ops)); string guard (make_guard (gp + hxx_name, *ctx)); @@ -285,6 +285,7 @@ generate (options const& ops, semantics::unit& unit, path const& p) switch (ops.database ()) { case database::mysql: + case database::sqlite: { relational::header::generate (); break; @@ -319,21 +320,7 @@ generate (options const& ops, semantics::unit& unit, path const& p) // { cxx_filter filt (ixx); - auto_ptr ctx; - - switch (ops.database ()) - { - case database::mysql: - { - ctx.reset (new relational::mysql::context (ixx, unit, ops)); - break; - } - case database::tracer: - { - ctx.reset (new context (ixx, unit, ops)); - break; - } - } + auto_ptr ctx (create_context (ixx, unit, ops)); // Copy prologue. // @@ -349,6 +336,7 @@ generate (options const& ops, semantics::unit& unit, path const& p) switch (ops.database ()) { case database::mysql: + case database::sqlite: { relational::inline_::generate (); break; @@ -373,6 +361,7 @@ generate (options const& ops, semantics::unit& unit, path const& p) // { cxx_filter filt (cxx); + auto_ptr ctx (create_context (cxx, unit, ops)); cxx << "#include " << endl << endl; @@ -393,14 +382,13 @@ generate (options const& ops, semantics::unit& unit, path const& p) switch (ops.database ()) { case database::mysql: + case database::sqlite: { - relational::mysql::context ctx (cxx, unit, ops); relational::source::generate (); break; } case database::tracer: { - context ctx (cxx, unit, ops); tracer::source::generate (); break; } @@ -422,6 +410,8 @@ generate (options const& ops, semantics::unit& unit, path const& p) // if (sql_schema) { + auto_ptr ctx (create_context (sql, unit, ops)); + // Copy prologue. // append (sql, ops.sql_prologue (), ops.sql_prologue_file ()); @@ -429,8 +419,8 @@ generate (options const& ops, semantics::unit& unit, path const& p) switch (ops.database ()) { case database::mysql: + case database::sqlite: { - relational::mysql::context ctx (sql, unit, ops); relational::schema::generate (); break; } diff --git a/odb/makefile b/odb/makefile index 9b83de9..af3d882 100644 --- a/odb/makefile +++ b/odb/makefile @@ -49,6 +49,15 @@ relational/mysql/header.cxx \ relational/mysql/source.cxx \ relational/mysql/schema.cxx +# Relational/SQLite +# +cxx_ptun += \ +relational/sqlite/common.cxx \ +relational/sqlite/context.cxx \ +relational/sqlite/header.cxx \ +relational/sqlite/source.cxx \ +relational/sqlite/schema.cxx + cxx_ptun += \ semantics/class.cxx \ semantics/class-template.cxx \ diff --git a/odb/odb.cxx b/odb/odb.cxx index 5dec9e5..87638a6 100644 --- a/odb/odb.cxx +++ b/odb/odb.cxx @@ -121,6 +121,7 @@ profile_paths (strings const& args, char const* name); static char const* const db_macro[] = { "-DODB_DATABASE_MYSQL", + "-DODB_DATABASE_SQLITE", "-DODB_DATABASE_TRACER" }; diff --git a/odb/option-functions.cxx b/odb/option-functions.cxx index 22a57f0..219f718 100644 --- a/odb/option-functions.cxx +++ b/odb/option-functions.cxx @@ -25,6 +25,11 @@ process_options (options& o) f.insert (schema_format::sql); break; } + case database::sqlite: + { + f.insert (schema_format::embedded); + break; + } case database::tracer: { break; diff --git a/odb/option-types.cxx b/odb/option-types.cxx index b05fead..dcac104 100644 --- a/odb/option-types.cxx +++ b/odb/option-types.cxx @@ -17,6 +17,7 @@ using namespace std; static const char* database_[] = { "mysql", + "sqlite", "tracer" }; diff --git a/odb/option-types.hxx b/odb/option-types.hxx index dc09780..75b3430 100644 --- a/odb/option-types.hxx +++ b/odb/option-types.hxx @@ -15,6 +15,7 @@ struct database // Keep in alphabetic order. // mysql, + sqlite, tracer }; diff --git a/odb/options.cli b/odb/options.cli index 12bb1e8..b536d21 100644 --- a/odb/options.cli +++ b/odb/options.cli @@ -48,8 +48,8 @@ class options ::database --database | -d { "", - "Generate code for the database. Valid values are \cb{mysql} and - \cb{tracer}." + "Generate code for the database. Valid values are \cb{mysql}, + \cb{sqlite}, and \cb{tracer}." }; bool --generate-query | -q @@ -374,4 +374,17 @@ class options database-default engine, pass \cb{default} as the value for this option." }; + + // + // SQLite-specific options. + // + + bool --sqlite-lax-auto-id + { + "Do not force monotonically increasing automatically-assigned + object ids. In this mode the generated database schema omits the + \cb{AUTOINCREMENT} keyword which results in faster object persistence + but may lead to automatically-assigned ids not being in a strictly + ascending order. Refer to the SQLite documentation for details." + }; }; diff --git a/odb/relational/sqlite/common.cxx b/odb/relational/sqlite/common.cxx new file mode 100644 index 0000000..31f0aad --- /dev/null +++ b/odb/relational/sqlite/common.cxx @@ -0,0 +1,448 @@ +// file : odb/relational/sqlite/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 sqlite + { + // + // 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 (column_sql_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_id:: + member_database_type_id (semantics::type* type, + string const& fq_type, + string const& key_prefix) + : relational::member_base (type, fq_type, key_prefix) + { + } + + string member_database_type_id:: + database_type_id (type& m) + { + type_id_.clear (); + member_base::traverse (m); + return type_id_; + } + + void member_database_type_id:: + traverse_composite (member_info&) + { + assert (false); + } + + void member_database_type_id:: + traverse_integer (member_info& mi) + { + size_t i ( + (mi.st->type - sql_type::TINYINT) * 2 + (mi.st->unsign ? 1 : 0)); + type_id_ = string ("mysql::") + integer_database_id[i]; + } + + void member_database_type_id:: + traverse_float (member_info& mi) + { + type_id_ = string ("mysql::") + + float_database_id[mi.st->type - sql_type::FLOAT]; + } + + void member_database_type_id:: + traverse_decimal (member_info&) + { + type_id_ = "mysql::id_decimal"; + } + + void member_database_type_id:: + traverse_date_time (member_info& mi) + { + type_id_ = string ("mysql::") + + date_time_database_id[mi.st->type - sql_type::DATE]; + } + + void member_database_type_id:: + traverse_string (member_info& mi) + { + type_id_ = string ("mysql::") + + char_bin_database_id[mi.st->type - sql_type::CHAR]; + } + + void member_database_type_id:: + traverse_bit (member_info&) + { + type_id_ = "mysql::id_bit"; + } + + void member_database_type_id:: + traverse_enum (member_info&) + { + type_id_ = "mysql::id_enum"; + } + + void member_database_type_id:: + traverse_set (member_info&) + { + type_id_ = "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_id (semantics::data_member& m) + { + return member_database_type_id_.database_type_id (m); + } + + private: + member_image_type member_image_type_; + member_database_type_id member_database_type_id_; + }; + entry query_columns_; + } +} diff --git a/odb/relational/sqlite/common.hxx b/odb/relational/sqlite/common.hxx new file mode 100644 index 0000000..f5009a0 --- /dev/null +++ b/odb/relational/sqlite/common.hxx @@ -0,0 +1,226 @@ +// file : odb/relational/sqlite/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_SQLITE_COMMON_HXX +#define ODB_RELATIONAL_SQLITE_COMMON_HXX + +#include +#include + +namespace relational +{ + namespace sqlite + { + 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 sqlite 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_id: member_base + { + member_database_type_id (semantics::type* type = 0, + string const& fq_type = string (), + string const& key_prefix = string ()); + string + database_type_id (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_id_; + }; + } +} +#endif // ODB_RELATIONAL_SQLITE_COMMON_HXX diff --git a/odb/relational/sqlite/context.cxx b/odb/relational/sqlite/context.cxx new file mode 100644 index 0000000..37588e9 --- /dev/null +++ b/odb/relational/sqlite/context.cxx @@ -0,0 +1,632 @@ +// file : odb/relational/sqlite/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 +#include + +using namespace std; + +namespace relational +{ + namespace sqlite + { + 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", "INTEGER", 0}, + + {"char", "INTEGER", 0}, + {"signed char", "INTEGER", 0}, + {"unsigned char", "INTEGER", 0}, + + {"short int", "INTEGER", 0}, + {"short unsigned int", "INTEGER", 0}, + + {"int", "INTEGER", 0}, + {"unsigned int", "INTEGER", 0}, + + {"long int", "INTEGER", 0}, + {"long unsigned int", "INTEGER", 0}, + + {"long long int", "INTEGER", 0}, + {"long long unsigned int", "INTEGER", 0}, + + {"float", "REAL", 0}, + {"double", "REAL", 0}, + + {"::std::string", "TEXT", 0} + }; + } + + context* context::current_; + + context:: + ~context () + { + if (current_ == this) + current_ = 0; + } + + 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_)) + { + assert (current_ == 0); + current_ = this; + + // 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_) + { + } + + 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 ("sqlite-grow")) + r_ = c.get ("sqlite-grow"); + else + { + // r_ should be false. + // + inherits (c); + + if (!r_) + names (c); + + c.set ("sqlite-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 ("sqlite-grow")) + return c.get ("sqlite-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. + // + + static sql_type + parse_sql_type (semantics::data_member& m, std::string const& sql); + + sql_type const& context:: + column_sql_type (semantics::data_member& m, string const& kp) + { + string key (kp.empty () + ? string ("sqlite-column-sql-type") + : "sqlite-" + kp + "-column-sql-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/sqlite/context.hxx b/odb/relational/sqlite/context.hxx new file mode 100644 index 0000000..0aff8f5 --- /dev/null +++ b/odb/relational/sqlite/context.hxx @@ -0,0 +1,120 @@ +// file : odb/relational/sqlite/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_SQLITE_CONTEXT_HXX +#define ODB_RELATIONAL_SQLITE_CONTEXT_HXX + +#include + +namespace relational +{ + namespace sqlite + { + 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& + column_sql_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&); + + public: + virtual + ~context (); + context (); + context (std::ostream&, semantics::unit&, options_type const&); + + static context& + current () + { + return *current_; + } + + private: + static context* current_; + + private: + struct data: base_context::data + { + data (std::ostream& os): base_context::data (os) {} + }; + + data* data_; + }; + } +} + +#endif // ODB_RELATIONAL_SQLITE_CONTEXT_HXX diff --git a/odb/relational/sqlite/header.cxx b/odb/relational/sqlite/header.cxx new file mode 100644 index 0000000..1ca038d --- /dev/null +++ b/odb/relational/sqlite/header.cxx @@ -0,0 +1,166 @@ +// file : odb/relational/sqlite/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 sqlite + { + 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/sqlite/schema.cxx b/odb/relational/sqlite/schema.cxx new file mode 100644 index 0000000..195a71e --- /dev/null +++ b/odb/relational/sqlite/schema.cxx @@ -0,0 +1,44 @@ +// file : odb/relational/sqlite/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 sqlite + { + namespace schema + { + namespace relational = relational::schema; + + // + // Create. + // + struct object_columns: relational::object_columns + { + object_columns (base const& x): base (x) {} + + virtual void + constraints (semantics::data_member& m) + { + base::constraints (m); + + if (m.count ("auto")) + { + if (options.sqlite_lax_auto_id ()) + os << " /*AUTOINCREMENT*/"; + else + os << " AUTOINCREMENT"; + } + } + + }; + entry object_columns_; + } + } +} diff --git a/odb/relational/sqlite/source.cxx b/odb/relational/sqlite/source.cxx new file mode 100644 index 0000000..9390a99 --- /dev/null +++ b/odb/relational/sqlite/source.cxx @@ -0,0 +1,899 @@ +// file : odb/relational/sqlite/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 sqlite + { + 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_id_ (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_id = member_database_type_id_.database_type_id (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_id = member_database_type_id_.database_type_id (mi.m); + + os << "{" + << "bool is_null;"; + } + + traits = "mysql::value_traits<\n " + + type + ",\n " + + image_type + ",\n " + + db_type_id + " >"; + } + + 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_id; + string member; + string image_type; + string traits; + + member_image_type member_image_type_; + member_database_type_id member_database_type_id_; + }; + 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_id_ (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_id = member_database_type_id_.database_type_id (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_id = member_database_type_id_.database_type_id (mi.m); + } + + traits = "mysql::value_traits<\n " + + type + ",\n " + + image_type + ",\n " + + db_type_id + " >"; + } + + 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_id; + string image_type; + string traits; + string member; + + member_image_type member_image_type_; + member_database_type_id member_database_type_id_; + }; + entry init_value_member_; + } + } +} -- cgit v1.1