summaryrefslogtreecommitdiff
path: root/odb
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2011-04-18 18:50:04 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2011-04-19 08:39:02 +0200
commitb904af7d7a716d243fb73e75b4a1cc404c9455f3 (patch)
tree75bfb4f9773623a50c6dcd4d6bdec53b0efba128 /odb
parentcdb791febe6c9ab2cfa36b1a07dfc71548674cf1 (diff)
Implement automatic mapping for C++ enums
Diffstat (limited to 'odb')
-rw-r--r--odb/relational/mysql/common.cxx7
-rw-r--r--odb/relational/mysql/context.cxx40
-rw-r--r--odb/relational/mysql/context.hxx7
-rw-r--r--odb/relational/mysql/header.cxx4
-rw-r--r--odb/relational/mysql/source.cxx133
-rw-r--r--odb/relational/sqlite/context.cxx24
-rw-r--r--odb/relational/sqlite/context.hxx6
7 files changed, 197 insertions, 24 deletions
diff --git a/odb/relational/mysql/common.cxx b/odb/relational/mysql/common.cxx
index 82acc42..c9a33e4 100644
--- a/odb/relational/mysql/common.cxx
+++ b/odb/relational/mysql/common.cxx
@@ -277,11 +277,12 @@ namespace relational
}
void member_image_type::
- traverse_enum (member_info&)
+ traverse_enum (member_info& mi)
{
- // Represented as string.
+ // Represented as either integer or string.
//
- type_ = "details::buffer";
+ type_ = "mysql::value_traits< " + mi.fq_type () +
+ ", mysql::id_enum >::image_type";
}
void member_image_type::
diff --git a/odb/relational/mysql/context.cxx b/odb/relational/mysql/context.cxx
index 11b5290..b413b85 100644
--- a/odb/relational/mysql/context.cxx
+++ b/odb/relational/mysql/context.cxx
@@ -230,6 +230,46 @@ namespace relational
return r;
}
+ string context::
+ database_type_impl (semantics::type& t,
+ string const& type,
+ semantics::context& ctx,
+ column_type_flags f)
+ {
+ string r (base_context::database_type_impl (t, type, ctx, f));
+
+ if (!r.empty ())
+ return r;
+
+ using semantics::enum_;
+
+ if (enum_* e = dynamic_cast<enum_*> (&t))
+ {
+ enum_::enumerates_iterator b (e->enumerates_begin ()),
+ end (e->enumerates_end ());
+
+ if (b != end)
+ {
+ r += "ENUM (";
+ for (enum_::enumerates_iterator i (b); i != end; ++i)
+ {
+ if (i != b)
+ r += ", ";
+
+ r += '\'';
+ r += i->enumerator ().name ();
+ r += '\'';
+ }
+ r += ")";
+
+ if ((f & ctf_default_null) == 0)
+ r += " NOT NULL";
+ }
+ }
+
+ return r;
+ }
+
//
// SQL type parsing.
//
diff --git a/odb/relational/mysql/context.hxx b/odb/relational/mysql/context.hxx
index 392ec77..cada5d9 100644
--- a/odb/relational/mysql/context.hxx
+++ b/odb/relational/mysql/context.hxx
@@ -95,6 +95,13 @@ namespace relational
virtual string
quote_id_impl (string const&) const;
+ protected:
+ virtual string
+ database_type_impl (semantics::type&,
+ string const& type,
+ semantics::context&,
+ column_type_flags);
+
public:
virtual
~context ();
diff --git a/odb/relational/mysql/header.cxx b/odb/relational/mysql/header.cxx
index 5210677..5abb241 100644
--- a/odb/relational/mysql/header.cxx
+++ b/odb/relational/mysql/header.cxx
@@ -136,7 +136,9 @@ namespace relational
virtual void
traverse_enum (member_info& mi)
{
- // Represented as string.
+ // Represented as either integer or string. Since we don't know
+ // at the code generation time which one it is, we have to always
+ // keep size in case it is a string.
//
os << image_type << " " << mi.var << "value;"
<< "unsigned long " << mi.var << "size;"
diff --git a/odb/relational/mysql/source.cxx b/odb/relational/mysql/source.cxx
index 41e4913..825a67e 100644
--- a/odb/relational/mysql/source.cxx
+++ b/odb/relational/mysql/source.cxx
@@ -62,6 +62,87 @@ namespace relational
}
//
+ //
+ //
+ struct object_columns: relational::object_columns, context
+ {
+ object_columns (base const& x): base (x) {}
+
+ virtual void
+ column (semantics::data_member& m,
+ string const& key_prefix,
+ string const& table,
+ string const& column)
+ {
+ // When we store a ENUM column in the MySQL database, if we bind
+ // an integer parameter, then it is treated as an index and if we
+ // bind a string, then it is treated as a enumerator. Everything
+ // would have worked well if the same logic applied to the load
+ // operation. That is, if we bind integer, then the database sends
+ // the index and if we bind string then the database sends the
+ // enumerator. Unfortunately, MySQL always sends the enumerator
+ // and to get the index one has to resort to the enum+0 hack.
+ //
+ // This causes the following problem: at code generation time we
+ // do not yet know which format we want. This is determined at
+ // C++ compile time by traits (the reason we don't know this is
+ // because we don't want to drag database-specific runtimes,
+ // which define the necessary traits, as well as their
+ // prerequisites into the ODB compilation process). As a result,
+ // we cannot decide at code generation time whether we need the
+ // +0 hack or not. One way to overcome this would be to construct
+ // the SELECT statements at runtime, something along these lines:
+ //
+ // "enum" + enum_traits<type>::hack + ","
+ //
+ // However, this complicates the code generator quite a bit: we
+ // either have to move to std::string storage for all the
+ // statements and all the databases, which is kind of a waste,
+ // or do some deep per-database customizations, which is hairy).
+ // So, instead, we are going to use another hack (hey, what the
+ // hell, right?) by loading both the index and enumerator
+ // combined into a string:
+ //
+ // CONCAT (enum+0, ' ', enum)
+ //
+ // For cases where we need the index, everything works since
+ // MySQL will convert the leading number and stop at the space.
+ // For cases where we need the enumerator, we do a bit of pre-
+ // processing (see enum_traits) before handing the value off
+ // to value_traits.
+ //
+
+ if (!out_ || column_sql_type (m, key_prefix).type != sql_type::ENUM)
+ {
+ base::column (m, key_prefix, table, column);
+ return;
+ }
+
+ line_ += "CONCAT(";
+
+ if (!table.empty ())
+ {
+ line_ += table;
+ line_ += '.';
+ }
+
+ line_ += column;
+ line_ += "+0, ' ', ";
+
+ if (!table.empty ())
+ {
+ line_ += table;
+ line_ += '.';
+ }
+
+ line_ += column;
+
+ line_ += ")";
+ }
+ };
+ entry<object_columns> object_columns_;
+
+ //
// bind
//
@@ -216,14 +297,12 @@ namespace relational
virtual void
traverse_enum (member_info& mi)
{
- // Represented as a string.
+ // Represented as either integer or string.
//
- os << b << ".buffer_type = MYSQL_TYPE_STRING;"
- << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
- << b << ".buffer_length = static_cast<unsigned long> (" << endl
- << arg << "." << mi.var << "value.capacity ());"
- << b << ".length = &" << arg << "." << mi.var << "size;"
- << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ os << "mysql::enum_traits::bind (" << b << "," << endl
+ << arg << "." << mi.var << "value," << endl
+ << arg << "." << mi.var << "size," << endl
+ << "&" << arg << "." << mi.var << "null);";
}
virtual void
@@ -360,12 +439,17 @@ namespace relational
virtual void
traverse_enum (member_info& mi)
{
- // Represented as a string.
+ // Represented as either integer or string (and we don't know
+ // at the code generation time which one it is).
//
os << "if (" << e << ")" << endl
<< "{"
- << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
- << "grew = true;"
+ << "if (mysql::enum_traits::grow (" <<
+ "i." << mi.var << "value, " <<
+ "i." << mi.var << "size))" << endl
+ << "grew = true;" // String
+ << "else" << endl
+ << e << " = 0;" // Integer.
<< "}";
}
@@ -609,17 +693,14 @@ namespace relational
virtual void
traverse_enum (member_info& mi)
{
- // Represented as a string.
+ // Represented as either integer or string.
//
- os << "std::size_t size (0);"
- << "std::size_t cap (i." << mi.var << "value.capacity ());"
- << traits << "::set_image (" << endl
+ os << "if (mysql::enum_traits::set_image (" << endl
<< "i." << mi.var << "value," << endl
- << "size," << endl
+ << "i." << mi.var << "size," << endl
<< "is_null," << endl
- << member << ");"
- << "i." << mi.var << "size = static_cast<unsigned long> (size);"
- << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ << member << "))" << endl
+ << "grew = true;";
}
virtual void
@@ -846,9 +927,9 @@ namespace relational
virtual void
traverse_enum (member_info& mi)
{
- // Represented as a string.
+ // Represented as either integer or string.
//
- os << traits << "::set_value (" << endl
+ os << "mysql::enum_traits::set_value (" << endl
<< member << "," << endl
<< "i." << mi.var << "value," << endl
<< "i." << mi.var << "size," << endl
@@ -890,6 +971,18 @@ namespace relational
}
};
entry<class_> class_entry_;
+
+ struct include: relational::include, context
+ {
+ include (base const& x): base (x) {}
+
+ virtual void
+ extra_post ()
+ {
+ os << "#include <odb/mysql/enum.hxx>" << endl;
+ }
+ };
+ entry<include> include_;
}
}
}
diff --git a/odb/relational/sqlite/context.cxx b/odb/relational/sqlite/context.cxx
index 7803298..b2b3033 100644
--- a/odb/relational/sqlite/context.cxx
+++ b/odb/relational/sqlite/context.cxx
@@ -196,6 +196,30 @@ namespace relational
return r;
}
+ string context::
+ database_type_impl (semantics::type& t,
+ string const& type,
+ semantics::context& ctx,
+ column_type_flags f)
+ {
+ string r (base_context::database_type_impl (t, type, ctx, f));
+
+ if (!r.empty ())
+ return r;
+
+ using semantics::enum_;
+
+ if (t.is_a<semantics::enum_> ())
+ {
+ r = "INTEGER";
+
+ if ((f & ctf_default_null) == 0)
+ r += " NOT NULL";
+ }
+
+ return r;
+ }
+
//
// SQL type parsing.
//
diff --git a/odb/relational/sqlite/context.hxx b/odb/relational/sqlite/context.hxx
index 1aa18af..621c549 100644
--- a/odb/relational/sqlite/context.hxx
+++ b/odb/relational/sqlite/context.hxx
@@ -47,6 +47,12 @@ namespace relational
virtual bool
grow_impl (semantics::data_member&, semantics::type&, string const&);
+ protected:
+ virtual string
+ database_type_impl (semantics::type&,
+ string const& type,
+ semantics::context&,
+ column_type_flags);
public:
virtual
~context ();