diff options
Diffstat (limited to 'odb/relational/mysql')
-rw-r--r-- | odb/relational/mysql/context.cxx | 96 | ||||
-rw-r--r-- | odb/relational/mysql/context.hxx | 20 | ||||
-rw-r--r-- | odb/relational/mysql/model.cxx | 111 | ||||
-rw-r--r-- | odb/relational/mysql/schema.cxx | 171 |
4 files changed, 242 insertions, 156 deletions
diff --git a/odb/relational/mysql/context.cxx b/odb/relational/mysql/context.cxx index 08f2df5..8862edf 100644 --- a/odb/relational/mysql/context.cxx +++ b/odb/relational/mysql/context.cxx @@ -67,16 +67,19 @@ namespace relational } context:: - context (ostream& os, semantics::unit& u, options_type const& ops) + context (ostream& os, + semantics::unit& u, + options_type const& ops, + sema_rel::model* m) : root_context (os, u, ops, data_ptr (new (shared) data (os))), - base_context (static_cast<data*> (root_context::data_.get ())), + base_context (static_cast<data*> (root_context::data_.get ()), m), data_ (static_cast<data*> (base_context::data_)) { assert (current_ == 0); current_ = this; - data_->generate_grow_ = true; - data_->need_alias_as_ = true; + generate_grow = true; + need_alias_as = true; data_->bind_vector_ = "MYSQL_BIND*"; data_->truncated_vector_ = "my_bool*"; @@ -299,9 +302,6 @@ namespace relational // 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) { @@ -310,18 +310,30 @@ namespace relational : "mysql-" + kp + "-column-sql-type"); if (!m.count (key)) - m.set (key, parse_sql_type (m, column_type (m, kp))); + { + try + { + m.set (key, parse_sql_type (column_type (m, kp))); + } + catch (invalid_sql_type const& e) + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << ": error: " << e.message () << endl; + + throw operation_failed (); + } + } return m.get<sql_type> (key); } - static sql_type - parse_sql_type (semantics::data_member& m, string const& sql) + sql_type context:: + parse_sql_type (string const& sqlt) { try { sql_type r; - sql_lexer l (sql); + sql_lexer l (sqlt); // While most type names use single identifier, there are // a couple of exceptions to this rule: @@ -568,16 +580,13 @@ namespace relational 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; + { + throw invalid_sql_type ( + "unknown MySQL type '" + t.identifier () + "'"); + } else - cerr << " error: expected MySQL type name" << endl; - - throw operation_failed (); + throw invalid_sql_type ("expected MySQL type name"); } // Fall through. @@ -598,11 +607,9 @@ namespace relational { if (t.type () != sql_token::t_string_lit) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: string literal expected in MySQL ENUM " - << "or SET declaration" << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "string literal expected in MySQL ENUM or SET " + "declaration"); } if (r.type == sql_type::ENUM) @@ -614,11 +621,8 @@ namespace relational break; else if (t.punctuation () != sql_token::p_comma) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: comma expected in MySQL ENUM or " - << "SET declaration" << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "comma expected in MySQL ENUM or SET declaration"); } t = l.next (); @@ -628,11 +632,8 @@ namespace relational { 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 operation_failed (); + throw invalid_sql_type ( + "integer range expected in MySQL type declaration"); } unsigned int v; @@ -640,11 +641,9 @@ namespace relational if (!(is >> v && is.eof ())) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: invalid range value '" << t.literal () - << "'in MySQL type declaration" << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "invalid range value '" + t.literal () + "' in MySQL " + "type declaration"); } r.range = true; @@ -671,11 +670,8 @@ namespace relational if (t.punctuation () != sql_token::p_rparen) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: expected ')' in MySQL type declaration" - << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "expected ')' in MySQL type declaration"); } s = parse_sign; @@ -729,10 +725,7 @@ namespace relational if (r.type == sql_type::invalid) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: incomplete MySQL type declaration" << endl; - - throw operation_failed (); + throw invalid_sql_type ("incomplete MySQL type declaration"); } // If range is omitted for CHAR or BIT types, it defaults to 1. @@ -747,11 +740,8 @@ namespace relational } catch (sql_lexer::invalid_input const& e) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: invalid MySQL type declaration: " << e.message - << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "invalid MySQL type declaration: " + e.message); } } } diff --git a/odb/relational/mysql/context.hxx b/odb/relational/mysql/context.hxx index 26568ec..2615397 100644 --- a/odb/relational/mysql/context.hxx +++ b/odb/relational/mysql/context.hxx @@ -84,6 +84,21 @@ namespace relational column_sql_type (semantics::data_member&, string const& key_prefix = string ()); + public: + struct invalid_sql_type + { + invalid_sql_type (string const& message): message_ (message) {} + + string const& + message () const {return message_;} + + private: + string message_; + }; + + static sql_type + parse_sql_type (string const&); + protected: virtual bool grow_impl (semantics::class_&); @@ -106,7 +121,10 @@ namespace relational virtual ~context (); context (); - context (std::ostream&, semantics::unit&, options_type const&); + context (std::ostream&, + semantics::unit&, + options_type const&, + sema_rel::model*); static context& current () diff --git a/odb/relational/mysql/model.cxx b/odb/relational/mysql/model.cxx new file mode 100644 index 0000000..0ebf65f --- /dev/null +++ b/odb/relational/mysql/model.cxx @@ -0,0 +1,111 @@ +// file : odb/relational/mysql/model.cxx +// author : Boris Kolpackov <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include <sstream> + +#include <odb/relational/model.hxx> +#include <odb/relational/mysql/context.hxx> + +using namespace std; + +namespace relational +{ + namespace mysql + { + namespace model + { + namespace relational = relational::model; + + struct object_columns: relational::object_columns, context + { + object_columns (base const& x): base (x) {} + + virtual string + default_bool (semantics::data_member&, bool v) + { + // MySQL has TRUE and FALSE as just aliases for 1 and 0. Still + // use them for self-documentation. + // + return v ? "TRUE" : "FALSE"; + } + + virtual string + default_enum (semantics::data_member& m, tree en, string const& name) + { + // Make sure the column is mapped to an ENUM or integer type. + // + sql_type const& t (column_sql_type (m)); + + switch (t.type) + { + case sql_type::ENUM: + case sql_type::TINYINT: + case sql_type::SMALLINT: + case sql_type::MEDIUMINT: + case sql_type::INT: + case sql_type::BIGINT: + break; + default: + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << ": error: column with default value specified as C++ " + << "enumerator must map to MySQL ENUM or integer type" + << endl; + + throw operation_failed (); + } + } + + using semantics::enum_; + using semantics::enumerator; + + enumerator& er (dynamic_cast<enumerator&> (*unit.find (en))); + enum_& e (er.enum_ ()); + + if (t.type == sql_type::ENUM) + { + // Assuming the enumerators in the C++ enum and MySQL ENUM are + // in the same order, calculate the poistion of the C++ + // enumerator and use that as an index in the MySQL ENUM. + // + size_t pos (0); + + for (enum_::enumerates_iterator i (e.enumerates_begin ()), + end (e.enumerates_end ()); i != end; ++i) + { + if (&i->enumerator () == &er) + break; + + pos++; + } + + if (pos < t.enumerators.size ()) + return t.enumerators[pos]; + else + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << ": error: unable to map C++ enumerator '" << name + << "' to MySQL ENUM value" << endl; + + throw operation_failed (); + } + } + else + { + ostringstream ostr; + + if (e.unsigned_ ()) + ostr << er.value (); + else + ostr << static_cast<long long> (er.value ()); + + return ostr.str (); + } + } + }; + entry<object_columns> object_columns_; + } + } +} diff --git a/odb/relational/mysql/schema.cxx b/odb/relational/mysql/schema.cxx index 485fa9f..428db1c 100644 --- a/odb/relational/mysql/schema.cxx +++ b/odb/relational/mysql/schema.cxx @@ -20,143 +20,110 @@ namespace relational // Create. // - struct create_common: virtual relational::create_common + struct create_column: relational::create_column, context { - virtual void - create_table_post () - { - os << ")"; - - string const& engine (options.mysql_engine ()); - - if (engine != "default") - os << endl - << " ENGINE=" << engine; - - os << endl; - } - }; - - struct object_columns: relational::object_columns, context - { - object_columns (base const& x): base (x) {} + create_column (base const& x): base (x) {} virtual void - null (semantics::data_member& m) + null (sema_rel::column& c) { // MySQL TIMESTAMP is by default NOT NULL. If we want it // to contain NULL values, we need to explicitly declare // the column as NULL. // - if (context::null (m, prefix_) && - column_sql_type (m, prefix_).type == sql_type::TIMESTAMP) - os << " NULL"; - else - base::null (m); + if (c.null ()) + { + // This should never fail since we have already parsed this. + // + sql_type const& t (parse_sql_type (c.type ())); + + if (t.type == sql_type::TIMESTAMP) + { + os << " NULL"; + return; + } + } + + base::null (c); } virtual void - default_bool (semantics::data_member&, bool v) + auto_ (sema_rel::column&) { - // MySQL has TRUE and FALSE as just aliases for 1 and 0. Still - // use them for self-documentation. - // - os << " DEFAULT " << (v ? "TRUE" : "FALSE"); + os << " AUTO_INCREMENT"; } + }; + entry<create_column> create_column_; + + struct create_foreign_key: relational::create_foreign_key, context + { + create_foreign_key (base const& x): base (x) {} virtual void - default_enum (semantics::data_member& m, tree en, string const& name) + traverse (sema_rel::foreign_key& fk) { - // Make sure the column is mapped to an ENUM or integer type. + // MySQL does not support deferred constraint checking. Output + // such foreign keys as comments, for documentation, unless we + // are generating embedded schema. // - sql_type const& t (column_sql_type (m)); - - switch (t.type) - { - case sql_type::ENUM: - case sql_type::TINYINT: - case sql_type::SMALLINT: - case sql_type::MEDIUMINT: - case sql_type::INT: - case sql_type::BIGINT: - break; - default: - { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: column with default value specified as C++ " - << "enumerator must map to MySQL ENUM or integer type" - << endl; - - throw operation_failed (); - } - } - - using semantics::enum_; - using semantics::enumerator; - - enumerator& er (dynamic_cast<enumerator&> (*unit.find (en))); - enum_& e (er.enum_ ()); - - if (t.type == sql_type::ENUM) + if (fk.deferred ()) { - // Assuming the enumerators in the C++ enum and MySQL ENUM are - // in the same order, calculate the poistion of the C++ - // enumerator and use that as an index in the MySQL ENUM. + // Don't bloat C++ code with comment strings if we are + // generating embedded schema. // - size_t pos (0); - - for (enum_::enumerates_iterator i (e.enumerates_begin ()), - end (e.enumerates_end ()); i != end; ++i) + if (format_ != schema_format::embedded) { - if (&i->enumerator () == &er) - break; - - pos++; - } + os << endl + << endl + << " /*" << endl; - if (pos < t.enumerators.size ()) - os << " DEFAULT " << t.enumerators[pos]; - else - { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: unable to map C++ enumerator '" << name - << "' to MySQL ENUM value" << endl; + base::create (fk); - throw operation_failed (); + os << endl + << " */"; } } else - { - if (e.unsigned_ ()) - os << " DEFAULT " << er.value (); - else - os << " DEFAULT " << static_cast<long long> (er.value ()); - } + base::traverse (fk); } - virtual void - constraints (semantics::data_member& m) + virtual string + name (sema_rel::foreign_key& fk) { - base::constraints (m); - - if (m.count ("auto")) - os << " AUTO_INCREMENT"; + // In MySQL, foreign key names are database-global. Make them + // unique by prefixing the key name with table name. + // + return static_cast<sema_rel::table&> (fk.scope ()).name () + + '_' + fk.name (); } + virtual void + deferred () + { + // MySQL doesn't support deferred. + } }; - entry<object_columns> object_columns_; + entry<create_foreign_key> create_foreign_key_; - struct member_create: relational::member_create, create_common + struct create_table: relational::create_table, context { - member_create (base const& x): base (x) {} - }; - entry<member_create> member_create_; + create_table (base const& x): base (x) {} - struct class_create: relational::class_create, create_common - { - class_create (base const& x): base (x) {} + virtual void + create_post () + { + os << ")"; + + string const& engine (options.mysql_engine ()); + + if (engine != "default") + os << endl + << " ENGINE=" << engine; + + os << endl; + } }; - entry<class_create> class_create_; + entry<create_table> create_table_; } } } |