From 81ea37904e4959414b53b225b4b5e56e1b561bdc Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 22 Jul 2011 14:49:29 +0200 Subject: Add pragma for setting type's or member's default value New pragma: default. New test: default. --- odb/relational/mysql/schema.cxx | 79 +++++++++++++++++++++++++ odb/relational/pgsql/schema.cxx | 37 ++++++++++++ odb/relational/schema.cxx | 122 +++++++++++++++++++++++++++++++++++++++ odb/relational/schema.hxx | 57 +++++++++++++++++- odb/relational/sqlite/schema.cxx | 26 ++++++++- 5 files changed, 318 insertions(+), 3 deletions(-) (limited to 'odb/relational') diff --git a/odb/relational/mysql/schema.cxx b/odb/relational/mysql/schema.cxx index 3848c75..4a7171f 100644 --- a/odb/relational/mysql/schema.cxx +++ b/odb/relational/mysql/schema.cxx @@ -58,6 +58,85 @@ namespace relational } virtual void + 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. + // + os << " DEFAULT " << (v ? "TRUE" : "FALSE"); + } + + virtual void + 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 generation_failed (); + } + } + + using semantics::enum_; + using semantics::enumerator; + + enumerator& er (dynamic_cast (*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 ()) + 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; + + throw generation_failed (); + } + } + else + { + if (e.unsigned_ ()) + os << " DEFAULT " << er.value (); + else + os << " DEFAULT " << static_cast (er.value ()); + } + } + + virtual void constraints (semantics::data_member& m) { base::constraints (m); diff --git a/odb/relational/pgsql/schema.cxx b/odb/relational/pgsql/schema.cxx index b7901e5..6530e82 100644 --- a/odb/relational/pgsql/schema.cxx +++ b/odb/relational/pgsql/schema.cxx @@ -77,6 +77,43 @@ namespace relational } virtual void + default_bool (semantics::data_member&, bool v) + { + os << " DEFAULT " << (v ? "TRUE" : "FALSE"); + } + + virtual void + default_enum (semantics::data_member& m, tree en, string const&) + { + // Make sure the column is mapped to an integer type. + // + switch (column_sql_type (m).type) + { + case sql_type::SMALLINT: + case sql_type::INTEGER: + 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 PostgreSQL integer type" << endl; + + throw generation_failed (); + } + } + + using semantics::enumerator; + + enumerator& e (dynamic_cast (*unit.find (en))); + + if (e.enum_ ().unsigned_ ()) + os << " DEFAULT " << e.value (); + else + os << " DEFAULT " << static_cast (e.value ()); + } + + virtual void reference (semantics::data_member&) { } diff --git a/odb/relational/schema.cxx b/odb/relational/schema.cxx index 6acb6dc..a347d49 100644 --- a/odb/relational/schema.cxx +++ b/odb/relational/schema.cxx @@ -3,6 +3,12 @@ // copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file +#include + +#include +#include +#include + #include #include @@ -14,6 +20,122 @@ namespace relational { namespace schema { + // object_columns + // + void object_columns:: + default_ (semantics::data_member& m) + { + string s; + tree n (0); + + semantics::type& t (m.type ()); + + if (m.count ("default")) + { + s = m.get ("default"); + + // Empty string is a default value override which means + // there is no default value. + // + if (s.empty ()) + return; + + if (m.count ("default-node")) + n = m.get ("default-node"); + } + else if (t.count ("default")) + { + s = t.get ("default"); + + if (s.empty ()) + return; + + if (t.count ("default-node")) + n = t.get ("default-node"); + } + else + return; // No default value for this column. + + // The first letter in the default value string identifies + // the type of the value. See pragma.cxx for details. + // + switch (s[0]) + { + case 'n': + { + default_null (m); + break; + } + case 't': + case 'f': + { + default_bool (m, s[0] == 't'); + break; + } + case '+': + case '-': + { + switch (TREE_CODE (n)) + { + case INTEGER_CST: + { + HOST_WIDE_INT hwl (TREE_INT_CST_LOW (n)); + HOST_WIDE_INT hwh (TREE_INT_CST_HIGH (n)); + + unsigned long long l (hwl); + unsigned long long h (hwh); + unsigned short width (HOST_BITS_PER_WIDE_INT); + + unsigned long long v ((h << width) + l); + + default_integer (m, v, s[0] == '-'); + break; + } + case REAL_CST: + { + double v; + + REAL_VALUE_TYPE d (TREE_REAL_CST (n)); + + if (REAL_VALUE_ISINF (d)) + v = numeric_limits::infinity (); + else if (REAL_VALUE_ISNAN (d)) + v = numeric_limits::quiet_NaN (); + else + { + char tmp[256]; + real_to_decimal (tmp, &d, sizeof (tmp), 0, true); + istringstream is (tmp); + is >> v; + } + + if (s[0] == '-') + v = -v; + + default_float (m, v); + break; + } + default: + assert (false); + } + break; + } + + case 's': + { + default_string (m, string (s, 1, string::npos)); + break; + } + case 'e': + { + default_enum (m, n, string (s, 1, string::npos)); + break; + } + default: + assert (false); + } + } + struct schema_emitter: emitter, context { virtual void diff --git a/odb/relational/schema.hxx b/odb/relational/schema.hxx index b218c2b..6ada960 100644 --- a/odb/relational/schema.hxx +++ b/odb/relational/schema.hxx @@ -190,8 +190,11 @@ namespace relational type (m); null (m); - constraints (m); - reference (m); + + // An id member cannot have a default value. + // + if (!m.count ("id")) + default_ (m); // If we have options, add them. // @@ -200,6 +203,9 @@ namespace relational if (!o.empty ()) os << " " << o; + constraints (m); + reference (m); + return true; } @@ -217,6 +223,49 @@ namespace relational } virtual void + default_null (semantics::data_member&) + { + os << " DEFAULT NULL"; + } + + virtual void + default_bool (semantics::data_member&, bool v) + { + // Most databases do not support boolean literals. Those that + // do should override this. + // + os << " DEFAULT " << (v ? "1" : "0"); + } + + virtual void + default_integer (semantics::data_member&, unsigned long long v, bool neg) + { + os << " DEFAULT " << (neg ? "-" : "") << v; + } + + virtual void + default_float (semantics::data_member&, double v) + { + os << " DEFAULT " << v; + } + + virtual void + default_string (semantics::data_member&, string const& v) + { + os << " DEFAULT " << quote_string (v); + } + + virtual void + default_enum (semantics::data_member&, + tree /*enumerator*/, + string const& /*name*/) + { + // Has to be implemented by the database-specific override. + // + assert (false); + } + + virtual void constraints (semantics::data_member& m) { if (m.count ("id")) @@ -234,6 +283,10 @@ namespace relational } protected: + void + default_ (semantics::data_member&); + + protected: string prefix_; }; diff --git a/odb/relational/sqlite/schema.cxx b/odb/relational/sqlite/schema.cxx index 195a71e..253772d 100644 --- a/odb/relational/sqlite/schema.cxx +++ b/odb/relational/sqlite/schema.cxx @@ -19,11 +19,35 @@ namespace relational // // Create. // - struct object_columns: relational::object_columns + struct object_columns: relational::object_columns, context { object_columns (base const& x): base (x) {} virtual void + default_enum (semantics::data_member& m, tree en, string const&) + { + // Make sure the column is mapped to INTEGER. + // + if (column_sql_type (m).type != sql_type::INTEGER) + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << ": error: column with default value specified as C++ " + << "enumerator must map to SQLite INTEGER" << endl; + + throw generation_failed (); + } + + using semantics::enumerator; + + enumerator& e (dynamic_cast (*unit.find (en))); + + if (e.enum_ ().unsigned_ ()) + os << " DEFAULT " << e.value (); + else + os << " DEFAULT " << static_cast (e.value ()); + } + + virtual void constraints (semantics::data_member& m) { base::constraints (m); -- cgit v1.1