diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2012-07-10 15:17:16 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2012-07-10 15:17:16 +0200 |
commit | b8554760aa3a5c5697c77d11e507a2bb46dbf8e4 (patch) | |
tree | 3f2bcb28a59eb0d4cce4586acec4a8c639cde7e6 /odb/relational/mysql | |
parent | 1b64460a2b2c5411b6052cd4c4d8e8b0d46a4086 (diff) |
Add support for custom database type mapping
New pragma qualifier, map, and specifiers: as, to, from. New tests:
<database>/custom.
Diffstat (limited to 'odb/relational/mysql')
-rw-r--r-- | odb/relational/mysql/context.cxx | 95 | ||||
-rw-r--r-- | odb/relational/mysql/context.hxx | 49 | ||||
-rw-r--r-- | odb/relational/mysql/model.cxx | 2 | ||||
-rw-r--r-- | odb/relational/mysql/source.cxx | 41 |
4 files changed, 128 insertions, 59 deletions
diff --git a/odb/relational/mysql/context.cxx b/odb/relational/mysql/context.cxx index ec1a967..1e44b34 100644 --- a/odb/relational/mysql/context.cxx +++ b/odb/relational/mysql/context.cxx @@ -106,6 +106,13 @@ namespace relational { } + string const& context:: + convert_expr (string const& sqlt, semantics::data_member& m, bool to) + { + sql_type const& t (parse_sql_type (sqlt, m)); + return to ? t.to : t.from; + } + string context:: quote_id_impl (qname const& id) const { @@ -319,20 +326,31 @@ namespace relational // sql_type const& context:: - parse_sql_type (string const& t, semantics::data_member& m) + parse_sql_type (string const& t, semantics::data_member& m, bool custom) { - // If this proves to be too expensive, we can maintain a - // cache of parsed types. + // If this proves to be too expensive, we can maintain a cache of + // parsed types across contexts. // data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t)); - if (i != data_->sql_type_cache_.end ()) - return i->second; + if (i != data_->sql_type_cache_.end () + && (custom ? i->second.custom_cached : i->second.straight_cached)) + { + return (custom ? i->second.custom : i->second.straight); + } else { try { - return (data_->sql_type_cache_[t] = parse_sql_type (t)); + sql_type st ( + parse_sql_type ( + t, + custom ? &unit.get<custom_db_types> ("custom-db-types") : 0)); + + if (custom) + return data_->sql_type_cache_[t].cache_custom (st); + else + return data_->sql_type_cache_[t].cache_straight (st); } catch (invalid_sql_type const& e) { @@ -344,12 +362,41 @@ namespace relational } } + inline sql_type + error (bool fail, string const& m) + { + if (!fail) + return sql_type (); + else + throw context::invalid_sql_type (m); + } + sql_type context:: - parse_sql_type (string const& sqlt) + parse_sql_type (string sqlt, custom_db_types const* ct) { try { sql_type r; + + // First run the type through the custom mapping, if requested. + // + if (ct != 0) + { + for (custom_db_types::const_iterator i (ct->begin ()); + i != ct->end (); ++i) + { + custom_db_type const& t (*i); + + if (t.type.match (sqlt)) + { + r.to = t.type.replace (sqlt, t.to); + r.from = t.type.replace (sqlt, t.from); + sqlt = t.type.replace (sqlt, t.as); + break; + } + } + } + sql_lexer l (sqlt); // While most type names use single identifier, there are @@ -599,11 +646,11 @@ namespace relational { if (tt == sql_token::t_identifier) { - throw invalid_sql_type ( - "unknown MySQL type '" + t.identifier () + "'"); + return error (ct, "unknown MySQL type '" + t.identifier () + + "'"); } else - throw invalid_sql_type ("expected MySQL type name"); + return error (ct, "expected MySQL type name"); } // Fall through. @@ -624,9 +671,8 @@ namespace relational { if (t.type () != sql_token::t_string_lit) { - throw invalid_sql_type ( - "string literal expected in MySQL ENUM or SET " - "declaration"); + return error (ct, "string literal expected in MySQL " + "ENUM or SET declaration"); } if (r.type == sql_type::ENUM) @@ -638,8 +684,8 @@ namespace relational break; else if (t.punctuation () != sql_token::p_comma) { - throw invalid_sql_type ( - "comma expected in MySQL ENUM or SET declaration"); + return error (ct, "comma expected in MySQL ENUM or " + "SET declaration"); } t = l.next (); @@ -649,8 +695,8 @@ namespace relational { if (t.type () != sql_token::t_int_lit) { - throw invalid_sql_type ( - "integer range expected in MySQL type declaration"); + return error (ct, "integer range expected in MySQL type " + "declaration"); } unsigned int v; @@ -658,9 +704,8 @@ namespace relational if (!(is >> v && is.eof ())) { - throw invalid_sql_type ( - "invalid range value '" + t.literal () + "' in MySQL " - "type declaration"); + return error (ct, "invalid range value '" + t.literal () + + "' in MySQL type declaration"); } r.range = true; @@ -687,8 +732,7 @@ namespace relational if (t.punctuation () != sql_token::p_rparen) { - throw invalid_sql_type ( - "expected ')' in MySQL type declaration"); + return error (ct, "expected ')' in MySQL type declaration"); } s = parse_sign; @@ -741,9 +785,7 @@ namespace relational } if (r.type == sql_type::invalid) - { - throw invalid_sql_type ("incomplete MySQL type declaration"); - } + return error (ct, "incomplete MySQL type declaration"); // If range is omitted for CHAR or BIT types, it defaults to 1. // @@ -757,8 +799,7 @@ namespace relational } catch (sql_lexer::invalid_input const& e) { - throw invalid_sql_type ( - "invalid MySQL type declaration: " + e.message); + return error (ct, "invalid MySQL type declaration: " + e.message); } } } diff --git a/odb/relational/mysql/context.hxx b/odb/relational/mysql/context.hxx index 8759987..0d76b04 100644 --- a/odb/relational/mysql/context.hxx +++ b/odb/relational/mysql/context.hxx @@ -75,14 +75,20 @@ namespace relational bool range; unsigned int range_value; // MySQL max value is 2^32 - 1 (LONGBLOG/TEXT). std::vector<std::string> enumerators; // Enumerator strings for ENUM. + + // Conversion expressions for custom database types. + // + std::string to; + std::string from; }; class context: public virtual relational::context { public: sql_type const& - parse_sql_type (string const&, semantics::data_member&); - + parse_sql_type (string const&, + semantics::data_member&, + bool custom = true); public: struct invalid_sql_type { @@ -95,10 +101,17 @@ namespace relational string message_; }; + // If custom_db_types is NULL, then this function returns + // invalid type instead of throwing in case an unknown type + // is encountered. + // static sql_type - parse_sql_type (string const&); + parse_sql_type (string, custom_db_types const* = 0); protected: + virtual string const& + convert_expr (string const&, semantics::data_member&, bool); + virtual bool grow_impl (semantics::class_&); @@ -140,7 +153,35 @@ namespace relational { data (std::ostream& os): base_context::data (os) {} - typedef std::map<string, sql_type> sql_type_cache; + struct sql_type_cache_entry + { + sql_type_cache_entry () + : custom_cached (false), straight_cached (false) {} + + sql_type const& + cache_custom (sql_type const& t) + { + custom = t; + custom_cached = true; + return custom; + } + + sql_type const& + cache_straight (sql_type const& t) + { + straight = t; + straight_cached = true; + return straight; + } + + sql_type custom; // With custom mapping. + sql_type straight; // Without custom mapping. + + bool custom_cached; + bool straight_cached; + }; + + typedef std::map<string, sql_type_cache_entry> sql_type_cache; sql_type_cache sql_type_cache_; }; data* data_; diff --git a/odb/relational/mysql/model.cxx b/odb/relational/mysql/model.cxx index 11ca157..a210c13 100644 --- a/odb/relational/mysql/model.cxx +++ b/odb/relational/mysql/model.cxx @@ -35,7 +35,7 @@ namespace relational { // Make sure the column is mapped to an ENUM or integer type. // - sql_type const& t (parse_sql_type (column_type (), m)); + sql_type const& t (parse_sql_type (column_type (), m, false)); switch (t.type) { diff --git a/odb/relational/mysql/source.cxx b/odb/relational/mysql/source.cxx index 8591aca..15a4fcf 100644 --- a/odb/relational/mysql/source.cxx +++ b/odb/relational/mysql/source.cxx @@ -109,7 +109,7 @@ namespace relational // to value_traits. // - string type (column_type ()); + string const& type (column_type ()); if (sk_ != statement_select || parse_sql_type (type, m).type != sql_type::ENUM) @@ -118,28 +118,18 @@ namespace relational return; } - string r; - - r += "CONCAT("; - - if (!table.empty ()) - { - r += table; - r += '.'; - } - - r += column; - r += "+0,' ',"; - + // Qualified column and conversion expression. + // + string qc; if (!table.empty ()) { - r += table; - r += '.'; + qc += table; + qc += '.'; } + qc += column; + qc = convert_from (qc, type, m); - r += column; - - r += ")"; + string r ("CONCAT(" + qc + "+0,' '," + qc + ")"); sc_.push_back ( relational::statement_column (table, r, type, m, key_prefix_)); @@ -158,7 +148,7 @@ namespace relational { // The same idea as in object_columns. // - string type (column_type ()); + string const& type (column_type ()); if (parse_sql_type (type, m).type != sql_type::ENUM) { @@ -166,14 +156,11 @@ namespace relational return; } - string r; - - r += "CONCAT("; - r += column; - r += "+0,' ',"; - r += column; - r += ")"; + // Column and conversion expression. + // + string c (convert_from (column, type, m)); + string r ("CONCAT(" + c + "+0,' '," + c + ")"); sc_.push_back (relational::statement_column (table, r, type, m)); } }; |