From ce03afff5ef2e8da677def73079864c31c6618d8 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 24 Jan 2013 15:10:22 +0200 Subject: Add support for mapping char[N] to CHAR/VARCHAR database types Also improve query support for arrays (decaying). --- odb/context.cxx | 23 ++++++------------ odb/context.hxx | 12 ++++++--- odb/relational/mssql/context.cxx | 47 +++++++++++++++++++++++++++++++++--- odb/relational/mssql/context.hxx | 2 +- odb/relational/mysql/context.cxx | 37 +++++++++++++++++++++++++--- odb/relational/mysql/context.hxx | 2 +- odb/relational/oracle/context.cxx | 51 ++++++++++++++++++++++++++++++++++++--- odb/relational/oracle/context.hxx | 2 +- odb/relational/pgsql/context.cxx | 39 +++++++++++++++++++++++++++--- odb/relational/pgsql/context.hxx | 2 +- odb/relational/processor.cxx | 23 +++++++++++++----- odb/relational/sqlite/context.cxx | 25 ++++++++++++++++--- odb/relational/sqlite/context.hxx | 2 +- 13 files changed, 222 insertions(+), 45 deletions(-) (limited to 'odb') diff --git a/odb/context.cxx b/odb/context.cxx index 46116f6..515e9a8 100644 --- a/odb/context.cxx +++ b/odb/context.cxx @@ -720,13 +720,6 @@ null (semantics::data_member& m) const } else pt = &t; - - // See if this is one of the types with built-in mapping. - // - type_map_type::const_iterator i (data_->type_map_.find (*pt, hint)); - - if (i != data_->type_map_.end ()) - return i->second.null; } } @@ -811,13 +804,6 @@ null (semantics::data_member& m, string const& kp) const } else pt = &t; - - // See if this is one of the types with built-in mapping. - // - type_map_type::const_iterator i (data_->type_map_.find (*pt, hint)); - - if (i != data_->type_map_.end ()) - return i->second.null; } } } @@ -1749,12 +1735,19 @@ find (semantics::type& t, semantics::names* hint) } string context:: -database_type_impl (semantics::type& t, semantics::names* hint, bool id) +database_type_impl (semantics::type& t, + semantics::names* hint, + bool id, + bool* null) { type_map_type::const_iterator i (data_->type_map_.find (t, hint)); if (i != data_->type_map_.end ()) + { + if (null != 0) + *null = i->second.null; return id ? i->second.id_type : i->second.type; + } else return string (); } diff --git a/odb/context.hxx b/odb/context.hxx index 83d0747..aa604e4 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -1088,19 +1088,23 @@ public: // protected: // Return empty string if there is no mapping. The type passed is - // already cvr-unqualified. + // already cvr-unqualified. The null out argument indicates whether + // the column should allow NULL values by default. // string - database_type (semantics::type& t, semantics::names* hint, bool id) + database_type (semantics::type& t, + semantics::names* hint, + bool id, + bool* null = 0) { - return current ().database_type_impl (t, hint, id); + return current ().database_type_impl (t, hint, id, null); } // The default implementation uses the type map (populated by the database- // specific context implementation) to come up with a mapping. // virtual string - database_type_impl (semantics::type&, semantics::names*, bool); + database_type_impl (semantics::type&, semantics::names*, bool, bool*); public: typedef context root_context; diff --git a/odb/relational/mssql/context.cxx b/odb/relational/mssql/context.cxx index c64ae57..d615a1c 100644 --- a/odb/relational/mssql/context.cxx +++ b/odb/relational/mssql/context.cxx @@ -30,7 +30,8 @@ namespace relational { {"bool", "BIT", 0, false}, - {"char", "TINYINT", 0, false}, + {"char", "CHAR(1)", 0, false}, + {"wchar_t", "NCHAR(1)", 0, false}, {"signed char", "TINYINT", 0, false}, {"unsigned char", "TINYINT", 0, false}, @@ -145,17 +146,57 @@ namespace relational } string context:: - database_type_impl (semantics::type& t, semantics::names* hint, bool id) + database_type_impl (semantics::type& t, + semantics::names* hint, + bool id, + bool* null) { - string r (base_context::database_type_impl (t, hint, id)); + string r (base_context::database_type_impl (t, hint, id, null)); if (!r.empty ()) return r; using semantics::enum_; + using semantics::array; + // Enum mapping. + // if (t.is_a ()) + { r = "INT"; + } + // char[N] mapping. + // + else if (array* a = dynamic_cast (&t)) + { + semantics::type& bt (a->base_type ()); + bool c (bt.is_a ()); + + if (c || bt.is_a ()) + { + unsigned long long n (a->size ()); + + if (n == 0) + return r; + if (n == 1) + r = c ? "CHAR(" : "NCHAR("; + else + { + r = c ? "VARCHAR(" : "NVARCHAR("; + n--; + } + + if (n > (c ? 8000 : 4000)) + r += "max)"; + else + { + ostringstream ostr; + ostr << n; + r += ostr.str (); + r += ')'; + } + } + } return r; } diff --git a/odb/relational/mssql/context.hxx b/odb/relational/mssql/context.hxx index ea451dc..e845b86 100644 --- a/odb/relational/mssql/context.hxx +++ b/odb/relational/mssql/context.hxx @@ -127,7 +127,7 @@ namespace relational protected: virtual string - database_type_impl (semantics::type&, semantics::names*, bool); + database_type_impl (semantics::type&, semantics::names*, bool, bool*); public: virtual diff --git a/odb/relational/mysql/context.cxx b/odb/relational/mysql/context.cxx index ba22733..4045f9c 100644 --- a/odb/relational/mysql/context.cxx +++ b/odb/relational/mysql/context.cxx @@ -31,7 +31,7 @@ namespace relational { {"bool", "TINYINT(1)", 0, false}, - {"char", "TINYINT", 0, false}, + {"char", "CHAR(1)", 0, false}, {"signed char", "TINYINT", 0, false}, {"unsigned char", "TINYINT UNSIGNED", 0, false}, @@ -269,16 +269,22 @@ namespace relational } string context:: - database_type_impl (semantics::type& t, semantics::names* hint, bool id) + database_type_impl (semantics::type& t, + semantics::names* hint, + bool id, + bool* null) { - string r (base_context::database_type_impl (t, hint, id)); + string r (base_context::database_type_impl (t, hint, id, null)); if (!r.empty ()) return r; using semantics::enum_; using semantics::enumerator; + using semantics::array; + // Enum mapping. + // if (enum_* e = dynamic_cast (&t)) { // We can only map to ENUM if the C++ enumeration is contiguous @@ -321,6 +327,31 @@ namespace relational r += " UNSIGNED"; } } + // char[N] mapping. + // + else if (array* a = dynamic_cast (&t)) + { + semantics::type& bt (a->base_type ()); + if (bt.is_a ()) + { + unsigned long long n (a->size ()); + + if (n == 0) + return r; + else if (n == 1) + r = "CHAR("; + else + { + r = "VARCHAR("; + n--; + } + + ostringstream ostr; + ostr << n; + r += ostr.str (); + r += ')'; + } + } return r; } diff --git a/odb/relational/mysql/context.hxx b/odb/relational/mysql/context.hxx index 0d76b04..fdcc8f6 100644 --- a/odb/relational/mysql/context.hxx +++ b/odb/relational/mysql/context.hxx @@ -127,7 +127,7 @@ namespace relational protected: virtual string - database_type_impl (semantics::type&, semantics::names*, bool); + database_type_impl (semantics::type&, semantics::names*, bool, bool*); public: virtual diff --git a/odb/relational/oracle/context.cxx b/odb/relational/oracle/context.cxx index df62ce8..7602dda 100644 --- a/odb/relational/oracle/context.cxx +++ b/odb/relational/oracle/context.cxx @@ -30,7 +30,7 @@ namespace relational { {"bool", "NUMBER(1)", 0, false}, - {"char", "NUMBER(3)", 0, false}, + {"char", "CHAR(1)", 0, false}, {"signed char", "NUMBER(3)", 0, false}, {"unsigned char", "NUMBER(3)", 0, false}, @@ -142,17 +142,62 @@ namespace relational } string context:: - database_type_impl (semantics::type& t, semantics::names* hint, bool id) + database_type_impl (semantics::type& t, + semantics::names* hint, + bool id, + bool* null) { - string r (base_context::database_type_impl (t, hint, id)); + string r (base_context::database_type_impl (t, hint, id, null)); if (!r.empty ()) return r; using semantics::enum_; + using semantics::array; + // Enum mapping. + // if (t.is_a ()) + { r = "NUMBER(10)"; + } + // char[N] mapping. + // + else if (array* a = dynamic_cast (&t)) + { + semantics::type& bt (a->base_type ()); + if (bt.is_a ()) + { + unsigned long long n (a->size ()); + + if (n == 0) + return r; + else if (n == 1) + r = "CHAR"; + else + { + r = "VARCHAR2"; + n--; + } + + // Oracle VARCHAR2 limit is 4000 bytes. Since there are no good + // alternatives (CLOB?), let the user specify the mapping. + // + if (n > 4000) + return ""; + + // Allow empty VARCHAR2 values. + // + if (null != 0 && r == "VARCHAR2") + *null = true; + + ostringstream ostr; + ostr << n; + r += '('; + r += ostr.str (); + r += ')'; + } + } return r; } diff --git a/odb/relational/oracle/context.hxx b/odb/relational/oracle/context.hxx index 0008724..a53e25d 100644 --- a/odb/relational/oracle/context.hxx +++ b/odb/relational/oracle/context.hxx @@ -121,7 +121,7 @@ namespace relational protected: virtual string - database_type_impl (semantics::type&, semantics::names*, bool); + database_type_impl (semantics::type&, semantics::names*, bool, bool*); public: virtual diff --git a/odb/relational/pgsql/context.cxx b/odb/relational/pgsql/context.cxx index 0f68281..95b30b0 100644 --- a/odb/relational/pgsql/context.cxx +++ b/odb/relational/pgsql/context.cxx @@ -31,7 +31,7 @@ namespace relational { {"bool", "BOOLEAN", 0, false}, - {"char", "SMALLINT", 0, false}, + {"char", "CHAR(1)", 0, false}, {"signed char", "SMALLINT", 0, false}, {"unsigned char", "SMALLINT", 0, false}, @@ -230,17 +230,50 @@ namespace relational } string context:: - database_type_impl (semantics::type& t, semantics::names* hint, bool id) + database_type_impl (semantics::type& t, + semantics::names* hint, + bool id, + bool* null) { - string r (base_context::database_type_impl (t, hint, id)); + string r (base_context::database_type_impl (t, hint, id, null)); if (!r.empty ()) return r; using semantics::enum_; + using semantics::array; + // Enum mapping. + // if (t.is_a ()) + { r = "INTEGER"; + } + // char[N] mapping. + // + else if (array* a = dynamic_cast (&t)) + { + semantics::type& bt (a->base_type ()); + if (bt.is_a ()) + { + unsigned long long n (a->size ()); + + if (n == 0) + return r; + else if (n == 1) + r = "CHAR("; + else + { + r = "VARCHAR("; + n--; + } + + ostringstream ostr; + ostr << n; + r += ostr.str (); + r += ')'; + } + } return r; } diff --git a/odb/relational/pgsql/context.hxx b/odb/relational/pgsql/context.hxx index e757b30..f607d54 100644 --- a/odb/relational/pgsql/context.hxx +++ b/odb/relational/pgsql/context.hxx @@ -113,7 +113,7 @@ namespace relational protected: virtual string - database_type_impl (semantics::type& t, semantics::names* hint, bool); + database_type_impl (semantics::type&, semantics::names*, bool, bool*); public: virtual diff --git a/odb/relational/processor.cxx b/odb/relational/processor.cxx index 3888b1e..6c36e61 100644 --- a/odb/relational/processor.cxx +++ b/odb/relational/processor.cxx @@ -144,19 +144,24 @@ namespace relational if (id_type.empty ()) id_type = database_type (t, hint, true); - if (type.empty ()) - type = database_type (t, hint, false); - if (id_type.empty () && wt != 0) id_type = database_type (*wt, whint, true); + bool null (false); + if (type.empty ()) + type = database_type (t, hint, false, &null); + if (type.empty () && wt != 0) - type = database_type (*wt, whint, false); + type = database_type (*wt, whint, false, &null); // Use id mapping for discriminators. // if (id (m) || discriminator (m)) type = id_type; + // Allow NULL if requested by the default mapping. + // + else if (null && !m.count ("not-null")) + m.set ("null", true); } if (kind == unknown && !type.empty ()) @@ -333,11 +338,17 @@ namespace relational if (type.empty () && wt != 0 && wt->count ("type")) type = wt->get ("type"); + bool null (false); if (type.empty ()) - type = database_type (t, hint, false); + type = database_type (t, hint, false, &null); if (type.empty () && wt != 0) - type = database_type (*wt, wh, false); + type = database_type (*wt, wh, false, &null); + + // Allow NULL if requested by the default mapping. + // + if (null && !m.count (prefix + "-not-null")) + m.set (prefix + "-null", true); } if (!type.empty ()) diff --git a/odb/relational/sqlite/context.cxx b/odb/relational/sqlite/context.cxx index 5e46c77..bb3d548 100644 --- a/odb/relational/sqlite/context.cxx +++ b/odb/relational/sqlite/context.cxx @@ -32,7 +32,7 @@ namespace relational { {"bool", "INTEGER", 0, false}, - {"char", "INTEGER", 0, false}, + {"char", "TEXT", 0, false}, {"signed char", "INTEGER", 0, false}, {"unsigned char", "INTEGER", 0, false}, @@ -219,17 +219,36 @@ namespace relational } string context:: - database_type_impl (semantics::type& t, semantics::names* hint, bool id) + database_type_impl (semantics::type& t, + semantics::names* hint, + bool id, + bool* null) { - string r (base_context::database_type_impl (t, hint, id)); + string r (base_context::database_type_impl (t, hint, id, null)); if (!r.empty ()) return r; using semantics::enum_; + using semantics::array; + // Enum mapping. + // if (t.is_a ()) + { r = "INTEGER"; + } + // char[N] mapping. + // + else if (array* a = dynamic_cast (&t)) + { + semantics::type& bt (a->base_type ()); + if (bt.is_a ()) + { + if (a->size () != 0) + r = "TEXT"; + } + } return r; } diff --git a/odb/relational/sqlite/context.hxx b/odb/relational/sqlite/context.hxx index 6da5b9b..ebba734 100644 --- a/odb/relational/sqlite/context.hxx +++ b/odb/relational/sqlite/context.hxx @@ -77,7 +77,7 @@ namespace relational protected: virtual string - database_type_impl (semantics::type&, semantics::names*, bool); + database_type_impl (semantics::type&, semantics::names*, bool, bool*); public: virtual -- cgit v1.1