From b8554760aa3a5c5697c77d11e507a2bb46dbf8e4 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 10 Jul 2012 15:17:16 +0200 Subject: Add support for custom database type mapping New pragma qualifier, map, and specifiers: as, to, from. New tests: /custom. --- odb/relational/oracle/common.cxx | 3 +- odb/relational/oracle/context.cxx | 139 ++++++++++++++++++++++---------------- odb/relational/oracle/context.hxx | 49 ++++++++++++-- odb/relational/oracle/model.cxx | 3 +- odb/relational/oracle/source.cxx | 2 +- 5 files changed, 132 insertions(+), 64 deletions(-) (limited to 'odb/relational/oracle') diff --git a/odb/relational/oracle/common.cxx b/odb/relational/oracle/common.cxx index 661bb4e..e9d8390 100644 --- a/odb/relational/oracle/common.cxx +++ b/odb/relational/oracle/common.cxx @@ -411,9 +411,10 @@ namespace relational { os << type << " (const char* t," << endl << "const char* c," << endl + << "const char* conv," << endl << "unsigned short p = 0xFFF," << endl << "short s = 0xFFF)" << endl - << " : " << base << " (t, c, p, s)" + << " : " << base << " (t, c, conv, p, s)" << "{" << "}"; } diff --git a/odb/relational/oracle/context.cxx b/odb/relational/oracle/context.cxx index cedf12d..c0a7199 100644 --- a/odb/relational/oracle/context.cxx +++ b/odb/relational/oracle/context.cxx @@ -104,6 +104,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 { @@ -162,20 +169,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") : 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) { @@ -187,12 +205,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 @@ -388,9 +435,8 @@ namespace relational (prefix == "TIMESTAMP WITH LOCAL TIME" || prefix == "TIMESTAMP WITH TIME")) { - throw invalid_sql_type ( - "Oracle timestamps with time zones are not currently " - "supported"); + return error (ct, "Oracle timestamps with time zones are " + "not currently supported"); } // // String and binary types. @@ -475,15 +521,10 @@ namespace relational // LONG types. // else if (id == "LONG") - { - throw invalid_sql_type ( - "Oracle LONG types are not supported"); - } + return error (ct, "Oracle LONG types are not supported"); else - { - throw invalid_sql_type ( - "unknown Oracle type '" + t.identifier () + "'"); - } + return error (ct, "unknown Oracle type '" + + t.identifier () + "'"); t = l.next (); continue; @@ -517,10 +558,8 @@ namespace relational r.prec_value = 6; } else - { - throw invalid_sql_type ( - "incomplete Oracle type declaration: '" + prefix + "'"); - } + return error (ct, "incomplete Oracle type declaration: '" + + prefix + "'"); // All of the possible types handled in this block can take // an optional precision specifier. Set the state and fall @@ -531,10 +570,8 @@ namespace relational else { assert (r.type == sql_type::invalid); - - throw invalid_sql_type ( - "unexepected '" + t.literal () + "' in Oracle " - "type declaration"); + return error (ct, "unexepected '" + t.literal () + + "' in Oracle type declaration"); } // Fall through. @@ -548,9 +585,8 @@ namespace relational if (t.type () != sql_token::t_int_lit) { - throw invalid_sql_type ( - "integer size/precision expected in Oracle type " - "declaration"); + return error (ct, "integer size/precision expected in " + "Oracle type declaration"); } // Parse the precision. @@ -561,9 +597,8 @@ namespace relational if (!(is >> v && is.eof ())) { - throw invalid_sql_type ( - "invalid prec value '" + t.literal () + "' in Oracle " - "type declaration"); + return error (ct, "invalid prec value '" + t.literal () + + "' in Oracle type declaration"); } // Store seconds precision in scale since prec holds @@ -591,16 +626,16 @@ namespace relational // if (r.type != sql_type::NUMBER) { - throw invalid_sql_type ( - "invalid scale in Oracle type declaration"); + return error (ct, "invalid scale in Oracle type " + "declaration"); } t = l.next (); if (t.type () != sql_token::t_int_lit) { - throw invalid_sql_type ( - "integer scale expected in Oracle type declaration"); + return error (ct, "integer scale expected in Oracle type " + "declaration"); } short v; @@ -608,9 +643,8 @@ namespace relational if (!(is >> v && is.eof ())) { - throw invalid_sql_type ( - "invalid scale value '" + t.literal () + "' in Oracle " - "type declaration"); + return error (ct, "invalid scale value '" + t.literal () + + "' in Oracle type declaration"); } r.scale = true; @@ -626,9 +660,8 @@ namespace relational r.byte_semantics = false; else if (id != "BYTE") { - throw invalid_sql_type ( - "invalid keyword '" + t.literal () + "' in Oracle " - "type declaration"); + return error (ct, "invalid keyword '" + t.literal () + + "' in Oracle type declaration"); } t = l.next (); @@ -636,8 +669,7 @@ namespace relational if (t.punctuation () != sql_token::p_rparen) { - throw invalid_sql_type ( - "expected ')' in Oracle type declaration"); + return error (ct, "expected ')' in Oracle type declaration"); } else t = l.next (); @@ -648,10 +680,8 @@ namespace relational } case parse_done: { - throw invalid_sql_type ( - "unexepected '" + t.literal () + "' in Oracle " - "type declaration"); - + return error (ct, "unexepected '" + t.literal () + "' in Oracle " + "type declaration"); break; } } @@ -687,23 +717,18 @@ namespace relational r.prec_value = 6; } else - { - throw invalid_sql_type ( - "incomplete Oracle type declaration: '" + prefix + "'"); - } + return error (ct, "incomplete Oracle type declaration: '" + + prefix + "'"); } else - { - throw invalid_sql_type ("invalid Oracle type declaration"); - } + return error (ct, "invalid Oracle type declaration"); } return r; } catch (sql_lexer::invalid_input const& e) { - throw invalid_sql_type ( - "invalid Oracle type declaration: " + e.message); + return error (ct, "invalid Oracle type declaration: " + e.message); } } } diff --git a/odb/relational/oracle/context.hxx b/odb/relational/oracle/context.hxx index 6b7e968..6b07a5f 100644 --- a/odb/relational/oracle/context.hxx +++ b/odb/relational/oracle/context.hxx @@ -69,14 +69,20 @@ namespace relational short scale_value; // Oracle min value is -84. Max value is 127. bool byte_semantics; + + // 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 { @@ -89,14 +95,21 @@ 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); public: static bool unsigned_integer (semantics::type&); protected: + virtual string const& + convert_expr (string const&, semantics::data_member&, bool); + virtual string quote_id_impl (qname const&) const; @@ -129,7 +142,35 @@ namespace relational { data (std::ostream& os): base_context::data (os) {} - typedef std::map 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 sql_type_cache; sql_type_cache sql_type_cache_; }; data* data_; diff --git a/odb/relational/oracle/model.cxx b/odb/relational/oracle/model.cxx index 4b874c8..5aa8e8d 100644 --- a/odb/relational/oracle/model.cxx +++ b/odb/relational/oracle/model.cxx @@ -28,7 +28,8 @@ namespace relational { // Make sure the column is mapped to Oracle NUMBER. // - if (parse_sql_type (column_type (), m).type != sql_type::NUMBER) + sql_type const& t (parse_sql_type (column_type (), m, false)); + if (t.type != sql_type::NUMBER) { cerr << m.file () << ":" << m.line () << ":" << m.column () << ": error: column with default value specified as C++ " diff --git a/odb/relational/oracle/source.cxx b/odb/relational/oracle/source.cxx index 1efe8be..cfbdbac 100644 --- a/odb/relational/oracle/source.cxx +++ b/odb/relational/oracle/source.cxx @@ -563,7 +563,7 @@ namespace relational { os << endl << strlit (" RETURNING " + - column_qname (*id) + + convert_from (column_qname (*id), *id) + " INTO " + qp.next ()); } -- cgit v1.1