aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--odb/context.hxx1
-rw-r--r--odb/generator.cxx2
-rw-r--r--odb/makefile3
-rw-r--r--odb/pragma.cxx338
-rw-r--r--odb/pragma.hxx9
-rw-r--r--odb/relational/common.cxx7
-rw-r--r--odb/relational/context.cxx16
-rw-r--r--odb/relational/context.hxx65
-rw-r--r--odb/relational/mssql/common.cxx3
-rw-r--r--odb/relational/mssql/context.cxx181
-rw-r--r--odb/relational/mssql/context.hxx48
-rw-r--r--odb/relational/mssql/model.cxx2
-rw-r--r--odb/relational/mssql/source.cxx6
-rw-r--r--odb/relational/mysql/context.cxx95
-rw-r--r--odb/relational/mysql/context.hxx49
-rw-r--r--odb/relational/mysql/model.cxx2
-rw-r--r--odb/relational/mysql/source.cxx41
-rw-r--r--odb/relational/oracle/common.cxx3
-rw-r--r--odb/relational/oracle/context.cxx139
-rw-r--r--odb/relational/oracle/context.hxx49
-rw-r--r--odb/relational/oracle/model.cxx3
-rw-r--r--odb/relational/oracle/source.cxx2
-rw-r--r--odb/relational/pgsql/context.cxx115
-rw-r--r--odb/relational/pgsql/context.hxx55
-rw-r--r--odb/relational/pgsql/model.cxx2
-rw-r--r--odb/relational/pgsql/source.cxx3
-rw-r--r--odb/relational/source.cxx23
-rw-r--r--odb/relational/source.hxx51
-rw-r--r--odb/relational/sqlite/context.cxx134
-rw-r--r--odb/relational/sqlite/context.hxx49
-rw-r--r--odb/relational/sqlite/model.cxx3
-rw-r--r--odb/relational/validator.cxx99
-rw-r--r--odb/relational/validator.hxx36
-rw-r--r--odb/validator.cxx102
34 files changed, 1269 insertions, 467 deletions
diff --git a/odb/context.hxx b/odb/context.hxx
index 0bb58ba..e4b9418 100644
--- a/odb/context.hxx
+++ b/odb/context.hxx
@@ -35,6 +35,7 @@ using std::cerr;
//
using cutl::re::regex;
using cutl::re::regexsub;
+typedef cutl::re::format regex_format;
typedef std::vector<regexsub> regex_mapping;
diff --git a/odb/generator.cxx b/odb/generator.cxx
index 2d787a1..690a375 100644
--- a/odb/generator.cxx
+++ b/odb/generator.cxx
@@ -522,7 +522,7 @@ generate (options const& ops,
//
throw failed ();
}
- catch (re::format const& e)
+ catch (regex_format const& e)
{
cerr << "error: invalid regex: '" << e.regex () << "': " <<
e.description () << endl;
diff --git a/odb/makefile b/odb/makefile
index 743ad0b..d906fa0 100644
--- a/odb/makefile
+++ b/odb/makefile
@@ -36,7 +36,8 @@ relational/inline.cxx \
relational/source.cxx \
relational/model.cxx \
relational/schema.cxx \
-relational/schema-source.cxx
+relational/schema-source.cxx \
+relational/validator.cxx
# Relational/MSSQL.
#
diff --git a/odb/pragma.cxx b/odb/pragma.cxx
index e2dc4cc..dcfab4e 100644
--- a/odb/pragma.cxx
+++ b/odb/pragma.cxx
@@ -13,7 +13,9 @@
#include <odb/pragma.hxx>
#include <odb/cxx-token.hxx>
#include <odb/cxx-lexer.hxx>
+
#include <odb/context.hxx>
+#include <odb/relational/context.hxx>
using namespace std;
using namespace cutl;
@@ -342,77 +344,6 @@ resolve_scoped_name (tree& token,
}
static bool
-check_qual_decl_type (tree d,
- string const& name,
- string const& p,
- location_t l)
-{
- int tc (TREE_CODE (d));
-
- if (p == "namespace")
- {
- if (tc != NAMESPACE_DECL)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a namespace" << endl;
- return false;
- }
- }
- else if (p == "object" ||
- p == "view")
- {
- if (tc != RECORD_TYPE)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a class" << endl;
- return false;
- }
- }
- else if (p == "value")
- {
- if (!TYPE_P (d))
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a type" << endl;
- return false;
- }
- }
- else if (p == "member")
- {
- if (tc != FIELD_DECL)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a data member" << endl;
- return false;
- }
- }
- else
- {
- error () << "unknown db pragma " << p << endl;
- return false;
- }
-
- return true;
-}
-
-static void
-add_qual_entry (compiler::context& ctx,
- string const& k,
- any const& v,
- location_t l)
-{
- // Store the TYPE_DECL node that was referred to in the pragma. This
- // can be used later as a name hint in case the type is a template
- // instantiation. Also store the pragma location which is used as
- // the "definition point" for this instantiation.
- //
- ctx.set ("tree-node", v);
- ctx.set ("location", l);
-
- ctx.set (k, true);
-}
-
-static bool
check_spec_decl_type (tree d,
string const& name,
string const& p,
@@ -632,6 +563,7 @@ static void
handle_pragma (cpp_reader* reader,
string const& p,
string const& qualifier,
+ any& qualifier_value,
tree decl,
string const& decl_name,
bool ns) // True if this is a position namespace pragma.
@@ -1418,6 +1350,93 @@ handle_pragma (cpp_reader* reader,
adder = &accumulate<string>;
tt = pragma_lex (&t);
}
+ else if (qualifier == "map" &&
+ (p == "type" ||
+ p == "as" ||
+ p == "to" ||
+ p == "from"))
+ {
+ // type("<regex>")
+ // as("<subst>")
+ // to("<subst>")
+ // from("<subst>")
+ //
+ using relational::custom_db_type;
+
+ // Make sure we've got the correct declaration type.
+ //
+ assert (decl == global_namespace);
+ custom_db_type& ct (qualifier_value.value<custom_db_type> ());
+
+ if (pragma_lex (&t) != CPP_OPEN_PAREN)
+ {
+ error () << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = pragma_lex (&t);
+
+ if (p == "type")
+ {
+ if (tt != CPP_STRING)
+ {
+ error () << "type name regex expected in db pragma " << p << endl;
+ return;
+ }
+
+ try
+ {
+ // Make it case-insensitive.
+ //
+ ct.type.assign (TREE_STRING_POINTER (t), true);
+ }
+ catch (regex_format const& e)
+ {
+ error () << "invalid regex: '" << e.regex () << "' in db pragma " <<
+ p << ": " << e.description () << endl;
+ return;
+ }
+ }
+ else if (p == "as")
+ {
+ if (tt != CPP_STRING)
+ {
+ error () << "type name expected in db pragma " << p << endl;
+ return;
+ }
+
+ ct.as = TREE_STRING_POINTER (t);
+ }
+ else if (p == "to")
+ {
+ if (tt != CPP_STRING)
+ {
+ error () << "expression expected in db pragma " << p << endl;
+ return;
+ }
+
+ ct.to = TREE_STRING_POINTER (t);
+ }
+ else if (p == "from")
+ {
+ if (tt != CPP_STRING)
+ {
+ error () << "expression expected in db pragma " << p << endl;
+ return;
+ }
+
+ ct.from = TREE_STRING_POINTER (t);
+ }
+
+ if (pragma_lex (&t) != CPP_CLOSE_PAREN)
+ {
+ error () << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ name.clear (); // We don't need to add anything for this pragma.
+ tt = pragma_lex (&t);
+ }
else if (p == "type" ||
p == "id_type" ||
p == "value_type" ||
@@ -1691,35 +1710,124 @@ handle_pragma (cpp_reader* reader,
return;
}
- // If the value is not specified and we don't use a custom adder,
- // then make it bool (flag).
+ // Add the pragma unless was indicated otherwise.
//
- if (adder == 0 && val.empty ())
- val = true;
+ if (!name.empty ())
+ {
+ // If the value is not specified and we don't use a custom adder,
+ // then make it bool (flag).
+ //
+ if (adder == 0 && val.empty ())
+ val = true;
- // Convert '_' to '-' in the context name so we get foo-bar instead
- // of foo_bar (that's the convention used).
- //
- for (size_t i (0); i < name.size (); ++i)
- if (name[i] == '_')
- name[i] = '-';
+ // Convert '_' to '-' in the context name so we get foo-bar instead
+ // of foo_bar (that's the convention used).
+ //
+ for (size_t i (0); i < name.size (); ++i)
+ if (name[i] == '_')
+ name[i] = '-';
- // Record this pragma.
- //
- add_pragma (
- pragma (p, name, val, loc, &check_spec_decl_type, adder), decl, ns);
+ // Record this pragma.
+ //
+ add_pragma (
+ pragma (p, name, val, loc, &check_spec_decl_type, adder), decl, ns);
+ }
// See if there are any more pragmas.
//
if (tt == CPP_NAME)
{
- handle_pragma (
- reader, IDENTIFIER_POINTER (t), qualifier, decl, decl_name, ns);
+ handle_pragma (reader,
+ IDENTIFIER_POINTER (t),
+ qualifier,
+ qualifier_value,
+ decl,
+ decl_name,
+ ns);
}
else if (tt != CPP_EOF)
error () << "unexpected text after " << p << " in db pragma" << endl;
}
+//
+// Qualifiers.
+//
+
+static bool
+check_qual_decl_type (tree d,
+ string const& name,
+ string const& p,
+ location_t l)
+{
+ int tc (TREE_CODE (d));
+
+ if (p == "map")
+ {
+ assert (d == global_namespace);
+ }
+ else if (p == "namespace")
+ {
+ if (tc != NAMESPACE_DECL)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a namespace" << endl;
+ return false;
+ }
+ }
+ else if (p == "object" ||
+ p == "view")
+ {
+ if (tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a class" << endl;
+ return false;
+ }
+ }
+ else if (p == "value")
+ {
+ if (!TYPE_P (d))
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type" << endl;
+ return false;
+ }
+ }
+ else if (p == "member")
+ {
+ if (tc != FIELD_DECL)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member" << endl;
+ return false;
+ }
+ }
+ else
+ {
+ error () << "unknown db pragma " << p << endl;
+ return false;
+ }
+
+ return true;
+}
+
+static void
+add_qual_entry (compiler::context& ctx,
+ string const& k,
+ any const& v,
+ location_t l)
+{
+ // Store the TYPE_DECL node that was referred to in the pragma. This
+ // can be used later as a name hint in case the type is a template
+ // instantiation. Also store the pragma location which is used as
+ // the "definition point" for this instantiation.
+ //
+ ctx.set ("tree-node", v);
+ ctx.set ("location", l);
+
+ ctx.set (k, true);
+}
+
static void
handle_pragma_qualifier (cpp_reader* reader, string const& p)
{
@@ -1728,12 +1836,30 @@ handle_pragma_qualifier (cpp_reader* reader, string const& p)
tree decl (0), orig_decl (0);
string decl_name;
- location_t loc (input_location);
- bool ns (false); // Namespace location pragma.
+
+ string name (p); // Pragma name.
+ any val; // Pragma value.
+ location_t loc (input_location); // Pragma location.
+ pragma::add_func adder (0); // Custom context adder.
+ bool ns (false); // Namespace location pragma.
// Pragma qualifiers.
//
- if (p == "namespace")
+ if (p == "map")
+ {
+ // map type("<regex>") as("<subst>") [to("<subst>")] [from("<subst>")]
+ //
+ using relational::custom_db_type;
+
+ custom_db_type ct;
+ ct.loc = loc;
+ val = ct;
+ name = "custom-db-types";
+ orig_decl = decl = global_namespace;
+ adder = &accumulate<custom_db_type>;
+ tt = pragma_lex (&t);
+ }
+ else if (p == "namespace")
{
// namespace [(<identifier>)]
// namespace () (refers to global namespace)
@@ -1917,7 +2043,7 @@ handle_pragma_qualifier (cpp_reader* reader, string const& p)
p == "transient" ||
p == "version")
{
- handle_pragma (reader, p, "member", 0, "", false);
+ handle_pragma (reader, p, "member", val, 0, "", false);
return;
}
else
@@ -1926,17 +2052,22 @@ handle_pragma_qualifier (cpp_reader* reader, string const& p)
return;
}
+ if (adder == 0)
+ val = orig_decl;
+
// Record this pragma.
//
pragma prag (p,
- p, // For now no need to translate '_' to '-'.
- any (orig_decl),
+ name, // For now no need to translate '_' to '-'.
+ val,
loc,
&check_qual_decl_type,
- &add_qual_entry);
+ adder != 0 ? adder : &add_qual_entry);
+
+ any* pval;
if (decl)
- decl_pragmas_[decl].insert (prag);
+ pval = &decl_pragmas_[decl].insert (prag).value;
else
{
tree scope (current_scope ());
@@ -1946,23 +2077,35 @@ handle_pragma_qualifier (cpp_reader* reader, string const& p)
if (!CLASS_TYPE_P (scope))
scope = global_namespace;
- loc_pragmas_[scope].push_back (prag);
+ pragma_list& pl (loc_pragmas_[scope]);
+ pl.push_back (prag);
+ pval = &pl.back ().value;
}
else
+ {
ns_loc_pragmas_.push_back (ns_loc_pragma (scope, prag));
+ pval = &ns_loc_pragmas_.back ().pragma.value;
+ }
}
// See if there are any more pragmas.
//
if (tt == CPP_NAME)
{
- handle_pragma (reader, IDENTIFIER_POINTER (t), p, decl, decl_name, ns);
+ handle_pragma (
+ reader, IDENTIFIER_POINTER (t), p, *pval, decl, decl_name, ns);
}
else if (tt != CPP_EOF)
error () << "unexpected text after " << p << " in db pragma" << endl;
}
extern "C" void
+handle_pragma_db_map (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "map");
+}
+
+extern "C" void
handle_pragma_db_namespace (cpp_reader* r)
{
handle_pragma_qualifier (r, "namespace");
@@ -2220,6 +2363,7 @@ register_odb_pragmas (void*, void*)
c_register_pragma_with_expansion (0, "db", handle_pragma_db);
/*
+ c_register_pragma_with_expansion ("db", "map", handle_pragma_db_map);
c_register_pragma_with_expansion ("db", "namespace", handle_pragma_db_namespace);
c_register_pragma_with_expansion ("db", "object", handle_pragma_db_object);
c_register_pragma_with_expansion ("db", "view", handle_pragma_db_view);
diff --git a/odb/pragma.hxx b/odb/pragma.hxx
index a71c49c..ac64346 100644
--- a/odb/pragma.hxx
+++ b/odb/pragma.hxx
@@ -74,16 +74,17 @@ struct pragma_set: std::set<pragma>
{
typedef std::set<pragma> base;
- void
+ pragma&
insert (pragma const& p)
{
std::pair<iterator, bool> r (base::insert (p));
+ pragma& x (const_cast<pragma&> (*r.first));
+
if (!r.second)
- {
- pragma& x (const_cast<pragma&> (*r.first));
x = p;
- }
+
+ return x;
}
template <typename I>
diff --git a/odb/relational/common.cxx b/odb/relational/common.cxx
index 469eef1..1e1009d 100644
--- a/odb/relational/common.cxx
+++ b/odb/relational/common.cxx
@@ -350,8 +350,8 @@ namespace relational
void query_columns::
column_ctor (string const& type, string const& base)
{
- os << type << " (const char* t, const char* c)" << endl
- << " : " << base << " (t, c)"
+ os << type << " (const char* t, const char* c, const char* conv)" << endl
+ << " : " << base << " (t, c, conv)"
<< "{"
<< "}";
}
@@ -389,6 +389,9 @@ namespace relational
<< scope_ << "::" << endl
<< name << " (A::" << "table_name, " << strlit (quote_id (column));
+ string const& conv (convert_to_expr (column_type (), m));
+ os << ", " << (conv.empty () ? "0" : strlit (conv));
+
column_ctor_extra (m);
os << ");"
diff --git a/odb/relational/context.cxx b/odb/relational/context.cxx
index 8630a30..08e68e5 100644
--- a/odb/relational/context.cxx
+++ b/odb/relational/context.cxx
@@ -44,6 +44,22 @@ namespace relational
current_ = this;
}
+ string context::
+ convert (string const& e, string const& c)
+ {
+ size_t p (c.find ("(?)"));
+ string r (c, 0, p);
+ r += e;
+ r.append (c, p + 3, string::npos);
+ return r;
+ }
+
+ string const& context::
+ convert_expr (string const&, semantics::data_member&, bool)
+ {
+ assert (false);
+ }
+
bool context::
grow_impl (semantics::class_&)
{
diff --git a/odb/relational/context.hxx b/odb/relational/context.hxx
index 60ed934..ca55650 100644
--- a/odb/relational/context.hxx
+++ b/odb/relational/context.hxx
@@ -23,6 +23,19 @@ namespace relational
statement_where // WHERE clause.
};
+ // Custom database type mapping.
+ //
+ struct custom_db_type
+ {
+ regex type;
+ std::string as;
+ std::string to;
+ std::string from;
+ location_t loc;
+ };
+
+ typedef std::vector<custom_db_type> custom_db_types;
+
class context: public virtual ::context
{
public:
@@ -94,6 +107,58 @@ namespace relational
return quote_id (table_name (m, p));
}
+ // Custom database type conversion.
+ //
+ public:
+ string
+ convert_to (string const& expr,
+ string const& sqlt,
+ semantics::data_member& m)
+ {
+ string const& conv (current ().convert_expr (sqlt, m, true));
+ return conv.empty () ? expr : convert (expr, conv);
+ }
+
+ string
+ convert_from (string const& expr,
+ string const& sqlt,
+ semantics::data_member& m)
+ {
+ string const& conv (current ().convert_expr (sqlt, m, false));
+ return conv.empty () ? expr : convert (expr, conv);
+ }
+
+ // These shortcut versions should only be used on special members
+ // (e.g., auto id, version, etc) since they may not determine the
+ // proper SQL type in other cases (prefixes, composite ids, etc).
+ //
+ string
+ convert_to (string const& expr, semantics::data_member& m)
+ {
+ return convert_to (expr, column_type (m), m);
+ }
+
+ string
+ convert_from (string const& expr, semantics::data_member& m)
+ {
+ return convert_from (expr, column_type (m), m);
+ }
+
+ // Return the conversion expression itself.
+ //
+ string const&
+ convert_to_expr (string const& sqlt, semantics::data_member& m)
+ {
+ return current ().convert_expr (sqlt, m, true);
+ }
+
+ protected:
+ virtual string const&
+ convert_expr (string const& sqlt, semantics::data_member&, bool to);
+
+ string
+ convert (string const& expr, string const& conv);
+
protected:
// The default implementation returns false.
//
diff --git a/odb/relational/mssql/common.cxx b/odb/relational/mssql/common.cxx
index 20be733..a327538 100644
--- a/odb/relational/mssql/common.cxx
+++ b/odb/relational/mssql/common.cxx
@@ -483,9 +483,10 @@ namespace relational
{
os << type << " (const char* t," << endl
<< "const char* c," << endl
+ << "const char* conv," << endl
<< "unsigned short p = 0," << endl
<< "unsigned short s = 0xFFFF)" << endl
- << " : " << base << " (t, c, p, s)"
+ << " : " << base << " (t, c, conv, p, s)"
<< "{"
<< "}";
}
diff --git a/odb/relational/mssql/context.cxx b/odb/relational/mssql/context.cxx
index 7917f93..c1fe9cc 100644
--- a/odb/relational/mssql/context.cxx
+++ b/odb/relational/mssql/context.cxx
@@ -109,6 +109,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
{
@@ -159,38 +166,68 @@ namespace relational
{
typedef context::invalid_sql_type invalid_sql_type;
- sql_parser (std::string const& sql)
- : l_ (sql)
- {
- }
+ sql_parser (custom_db_types const* ct): ct_ (ct) {}
sql_type
- parse ()
+ parse (std::string sql)
{
r_ = sql_type ();
+ m_.clear ();
+
+ // 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 (sql))
+ {
+ r_.to = t.type.replace (sql, t.to);
+ r_.from = t.type.replace (sql, t.from);
+ sql = t.type.replace (sql, t.as);
+ break;
+ }
+ }
+ }
+
+ l_.lex (sql);
+
+ bool ok (true);
try
{
- parse_name ();
+ ok = parse_name ();
}
catch (sql_lexer::invalid_input const& e)
{
- throw invalid_sql_type ("invalid SQL Server type declaration: " +
- e.message);
+ ok = false;
+ m_ = "invalid SQL Server type declaration: " + e.message;
+ }
+
+ if (!ok)
+ {
+ if (ct_ == 0)
+ return sql_type ();
+ else
+ throw invalid_sql_type (m_);
}
return r_;
}
- void
+ bool
parse_name ()
{
sql_token t (l_.next ());
if (t.type () != sql_token::t_identifier)
{
- throw invalid_sql_type ("expected SQL Server type name "
- "instead of '" + t.string () + "'");
+ m_ = "expected SQL Server type name instead of '" +
+ t.string () + "'";
+ return false;
}
string id (upcase (t.identifier ()));
@@ -228,7 +265,8 @@ namespace relational
r_.has_scale = true;
r_.scale = 0;
- parse_precision (l_.next ());
+ if (!parse_precision (l_.next ()))
+ return false;
}
else if (id == "SMALLMONEY")
{
@@ -252,7 +290,8 @@ namespace relational
r_.has_prec = true;
r_.prec = 53;
- parse_precision (l_.next ());
+ if (!parse_precision (l_.next ()))
+ return false;
}
else if (id == "DOUBLE")
{
@@ -261,8 +300,8 @@ namespace relational
if (t.type () != sql_token::t_identifier ||
upcase (t.identifier ()) != "PRECISION")
{
- throw invalid_sql_type ("expected 'PRECISION' instead of '"
- + t.string () + "'");
+ m_ = "expected 'PRECISION' instead of '" + t.string () + "'";
+ return false;
}
r_.type = sql_type::FLOAT;
@@ -273,12 +312,14 @@ namespace relational
// It appears that DOUBLE PRECISION can be followed by the
// precision specification.
//
- parse_precision (l_.next ());
+ if (!parse_precision (l_.next ()))
+ return false;
}
else if (id == "CHAR" ||
id == "CHARACTER")
{
- parse_char_trailer (false);
+ if (!parse_char_trailer (false))
+ return false;
}
else if (id == "VARCHAR")
{
@@ -287,7 +328,8 @@ namespace relational
r_.has_prec = true;
r_.prec = 1;
- parse_precision (l_.next ());
+ if (!parse_precision (l_.next ()))
+ return false;
}
else if (id == "TEXT")
{
@@ -300,7 +342,8 @@ namespace relational
r_.has_prec = true;
r_.prec = 1;
- parse_precision (l_.next ());
+ if (!parse_precision (l_.next ()))
+ return false;
}
else if (id == "NVARCHAR")
{
@@ -309,7 +352,8 @@ namespace relational
r_.has_prec = true;
r_.prec = 1;
- parse_precision (l_.next ());
+ if (!parse_precision (l_.next ()))
+ return false;
}
else if (id == "NTEXT")
{
@@ -329,13 +373,14 @@ namespace relational
else if (id == "CHAR" ||
id == "CHARACTER")
{
- parse_char_trailer (true);
+ if (!parse_char_trailer (true))
+ return false;
}
else
{
- throw invalid_sql_type (
- "expected 'CHAR', 'CHARACTER', or 'TEXT' instead of '"
- + t.string () + "'");
+ m_ = "expected 'CHAR', 'CHARACTER', or 'TEXT' instead of '"
+ + t.string () + "'";
+ return false;
}
}
else if (id == "BINARY")
@@ -358,7 +403,8 @@ namespace relational
r_.has_prec = true;
r_.prec = 1;
- parse_precision (t);
+ if (!parse_precision (t))
+ return false;
}
else if (id == "VARBINARY")
{
@@ -367,7 +413,8 @@ namespace relational
r_.has_prec = true;
r_.prec = 1;
- parse_precision (l_.next ());
+ if (!parse_precision (l_.next ()))
+ return false;
}
else if (id == "IMAGE")
{
@@ -384,7 +431,8 @@ namespace relational
r_.has_scale = true;
r_.scale = 7;
- parse_precision (l_.next ());
+ if (!parse_precision (l_.next ()))
+ return false;
}
else if (id == "DATETIME")
{
@@ -397,7 +445,8 @@ namespace relational
r_.has_scale = true;
r_.scale = 7;
- parse_precision (l_.next ());
+ if (!parse_precision (l_.next ()))
+ return false;
}
else if (id == "SMALLDATETIME")
{
@@ -410,7 +459,8 @@ namespace relational
r_.has_scale = true;
r_.scale = 7;
- parse_precision (l_.next ());
+ if (!parse_precision (l_.next ()))
+ return false;
}
else if (id == "UNIQUEIDENTIFIER")
{
@@ -423,12 +473,14 @@ namespace relational
}
else
{
- throw invalid_sql_type ("unexpected SQL Server type name '" +
- t.identifier () + "'");
+ m_ = "unexpected SQL Server type name '" + t.identifier () + "'";
+ return false;
}
+
+ return true;
}
- void
+ bool
parse_precision (sql_token t)
{
if (t.punctuation () == sql_token::p_lparen)
@@ -450,9 +502,9 @@ namespace relational
if (!(is >> v && is.eof ()))
{
- throw invalid_sql_type (
- "invalid precision value '" + t.literal () + "' in SQL "
- "Server type declaration");
+ m_ = "invalid precision value '" + t.literal () + "' in SQL "
+ "Server type declaration";
+ return false;
}
switch (r_.type)
@@ -475,8 +527,8 @@ namespace relational
}
else
{
- throw invalid_sql_type (
- "integer precision expected in SQL Server type declaration");
+ m_ = "integer precision expected in SQL Server type declaration";
+ return false;
}
// Parse the scale if present.
@@ -489,25 +541,25 @@ namespace relational
//
if (r_.type != sql_type::DECIMAL)
{
- throw invalid_sql_type (
- "unexpected scale in SQL Server type declaration");
+ m_ = "unexpected scale in SQL Server type declaration";
+ return false;
}
t = l_.next ();
if (t.type () != sql_token::t_int_lit)
{
- throw invalid_sql_type (
- "integer scale expected in SQL Server type declaration");
+ m_ = "integer scale expected in SQL Server type declaration";
+ return false;
}
istringstream is (t.literal ());
if (!(is >> r_.scale && is.eof ()))
{
- throw invalid_sql_type (
- "invalid scale value '" + t.literal () + "' in SQL Server "
- "type declaration");
+ m_ = "invalid scale value '" + t.literal () + "' in SQL "
+ "Server type declaration";
+ return false;
}
r_.has_scale = true;
@@ -516,13 +568,15 @@ namespace relational
if (t.punctuation () != sql_token::p_rparen)
{
- throw invalid_sql_type (
- "expected ')' in SQL Server type declaration");
+ m_ = "expected ')' in SQL Server type declaration";
+ return false;
}
}
+
+ return true;
}
- void
+ bool
parse_char_trailer (bool nat)
{
sql_token t (l_.next ());
@@ -543,7 +597,7 @@ namespace relational
r_.has_prec = true;
r_.prec = 1;
- parse_precision (t);
+ return parse_precision (t);
}
private:
@@ -554,26 +608,39 @@ namespace relational
}
private:
+ custom_db_types const* ct_;
sql_lexer l_;
sql_type r_;
+ string m_; // Error message.
};
}
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)
{
@@ -586,10 +653,10 @@ namespace relational
}
sql_type context::
- parse_sql_type (string const& t)
+ parse_sql_type (string const& sqlt, custom_db_types const* ct)
{
- sql_parser p (t);
- return p.parse ();
+ sql_parser p (ct);
+ return p.parse (sqlt);
}
}
}
diff --git a/odb/relational/mssql/context.hxx b/odb/relational/mssql/context.hxx
index b0cba1e..00bb806 100644
--- a/odb/relational/mssql/context.hxx
+++ b/odb/relational/mssql/context.hxx
@@ -79,13 +79,20 @@ namespace relational
// 'max' as in VARCHAR(max).
bool has_scale;
unsigned short scale; // Max value is 38.
+
+ // 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
@@ -99,10 +106,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 const&, custom_db_types const* = 0);
protected:
+ virtual string const&
+ convert_expr (string const&, semantics::data_member&, bool);
+
virtual string
quote_id_impl (qname const&) const;
@@ -135,7 +149,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/mssql/model.cxx b/odb/relational/mssql/model.cxx
index c3f4948..1c4f407 100644
--- a/odb/relational/mssql/model.cxx
+++ b/odb/relational/mssql/model.cxx
@@ -28,7 +28,7 @@ namespace relational
{
// Make sure the column is mapped to an integer or DECIMAL type.
//
- switch (parse_sql_type (column_type (), m).type)
+ switch (parse_sql_type (column_type (), m, false).type)
{
case sql_type::BIT:
case sql_type::TINYINT:
diff --git a/odb/relational/mssql/source.cxx b/odb/relational/mssql/source.cxx
index 0816466..866d17d 100644
--- a/odb/relational/mssql/source.cxx
+++ b/odb/relational/mssql/source.cxx
@@ -908,14 +908,16 @@ namespace relational
{
if (p == persist_after_values)
os << endl
- << strlit ("; SELECT SCOPE_IDENTITY()");
+ << strlit ("; SELECT " +
+ convert_from ("SCOPE_IDENTITY()", *id));
return;
}
}
if (p == persist_after_columns)
- os << strlit (" OUTPUT INSERTED." + column_qname (*id)) << endl;
+ os << strlit (" OUTPUT " + convert_from (
+ "INSERTED." + column_qname (*id), *id)) << endl;
}
virtual void
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));
}
};
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> ("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<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/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 ());
}
diff --git a/odb/relational/pgsql/context.cxx b/odb/relational/pgsql/context.cxx
index ca2b3a5..e8cbabd 100644
--- a/odb/relational/pgsql/context.cxx
+++ b/odb/relational/pgsql/context.cxx
@@ -185,6 +185,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;
+ }
+
bool context::
grow_impl (semantics::class_& c)
{
@@ -239,20 +246,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)
{
@@ -264,12 +282,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
@@ -384,8 +431,8 @@ namespace relational
}
else if (id == "TIMETZ")
{
- throw invalid_sql_type (
- "PostgreSQL time zones are not currently supported");
+ return error (ct, "PostgreSQL time zones are not currently "
+ "supported");
}
else if (id == "TIMESTAMP")
{
@@ -393,8 +440,8 @@ namespace relational
}
else if (id == "TIMESTAMPTZ")
{
- throw invalid_sql_type (
- "PostgreSQL time zones are not currently supported");
+ return error (ct, "PostgreSQL time zones are not currently "
+ "supported");
}
//
// String and binary types.
@@ -460,13 +507,11 @@ namespace relational
if (r.type == sql_type::invalid)
{
- if (tt == sql_token::t_identifier)
- {
- throw invalid_sql_type (
- "unknown PostgreSQL type '" + t.identifier () + "'");
- }
- else
- throw invalid_sql_type ("expected PostgreSQL type name");
+ return error (
+ ct,
+ tt == sql_token::t_identifier
+ ? "unknown PostgreSQL type '" + t.identifier () + "'"
+ : "expected PostgreSQL type name");
}
// Fall through.
@@ -481,8 +526,8 @@ namespace relational
if (t.type () != sql_token::t_int_lit)
{
- throw invalid_sql_type (
- "integer range expected in PostgreSQL type declaration");
+ return error (ct, "integer range expected in PostgreSQL "
+ "type declaration");
}
unsigned int v;
@@ -490,9 +535,8 @@ namespace relational
if (!(is >> v && is.eof ()))
{
- throw invalid_sql_type (
- "invalid range value '" + t.literal () + "' in PostgreSQL "
- "type declaration");
+ return error (ct, "invalid range value '" + t.literal () +
+ "' in PostgreSQL type declaration");
}
r.range = true;
@@ -510,8 +554,8 @@ namespace relational
if (t.punctuation () != sql_token::p_rparen)
{
- throw invalid_sql_type (
- "expected ')' in PostgreSQL type declaration");
+ return error (ct, "expected ')' in PostgreSQL type "
+ "declaration");
}
s = parse_suffix;
@@ -548,9 +592,10 @@ namespace relational
if (id3 == "ZONE")
{
- throw invalid_sql_type (
- "PostgreSQL time zones are not currently "
- "supported");
+ // This code shall not fall through.
+ //
+ return error (ct, "PostgreSQL time zones are not "
+ "currently supported");
}
}
}
@@ -558,8 +603,11 @@ namespace relational
}
}
- s = parse_done;
- break;
+ return error (
+ ct,
+ tt == sql_token::t_identifier
+ ? "unknown PostgreSQL type '" + t.identifier () + "'"
+ : "unknown PostgreSQL type");
}
case parse_done:
{
@@ -592,9 +640,7 @@ namespace relational
}
if (r.type == sql_type::invalid)
- {
- throw invalid_sql_type ("incomplete PostgreSQL type declaration");
- }
+ return error (ct, "incomplete PostgreSQL type declaration");
// If range is omitted for CHAR or BIT types, it defaults to 1.
//
@@ -608,8 +654,7 @@ namespace relational
}
catch (sql_lexer::invalid_input const& e)
{
- throw invalid_sql_type (
- "invalid PostgreSQL type declaration: " + e.message);
+ return error (ct, "invalid PostgreSQL type declaration: " + e.message);
}
}
}
diff --git a/odb/relational/pgsql/context.hxx b/odb/relational/pgsql/context.hxx
index c572b01..e757b30 100644
--- a/odb/relational/pgsql/context.hxx
+++ b/odb/relational/pgsql/context.hxx
@@ -59,20 +59,26 @@ namespace relational
sql_type () : type (invalid), range (false) {}
core_type type;
- bool range;
- // VARBIT maximum length is 2^31 - 1 bit.
- // String types can hold a maximum of 1GB of data.
+ // VARBIT maximum length is 2^31 - 1 bit. String types can hold a
+ // maximum of 1GB of data.
//
+ bool range;
unsigned int range_value;
+
+ // 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
{
@@ -85,10 +91,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_&);
@@ -127,7 +140,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/pgsql/model.cxx b/odb/relational/pgsql/model.cxx
index 06a7dde..8191730 100644
--- a/odb/relational/pgsql/model.cxx
+++ b/odb/relational/pgsql/model.cxx
@@ -34,7 +34,7 @@ namespace relational
{
// Make sure the column is mapped to an integer type.
//
- switch (parse_sql_type (column_type (), m).type)
+ switch (parse_sql_type (column_type (), m, false).type)
{
case sql_type::SMALLINT:
case sql_type::INTEGER:
diff --git a/odb/relational/pgsql/source.cxx b/odb/relational/pgsql/source.cxx
index 7bb2f8a..6580c21 100644
--- a/odb/relational/pgsql/source.cxx
+++ b/odb/relational/pgsql/source.cxx
@@ -617,7 +617,8 @@ namespace relational
if (id != 0 && !poly_derived && id->count ("auto"))
{
os << endl
- << strlit (" RETURNING " + column_qname (*id));
+ << strlit (" RETURNING " +
+ convert_from (column_qname (*id), *id));
}
}
diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx
index 0a77b74..adc7a0e 100644
--- a/odb/relational/source.cxx
+++ b/odb/relational/source.cxx
@@ -577,7 +577,8 @@ traverse_object (type& c)
os << endl;
os << strlit ((i == b ? " WHERE " : " AND ") + table + "." +
- quote_id (i->name) + "=" + qp->next ());
+ quote_id (i->name) + "=" +
+ convert_to (qp->next (), i->type, *i->member));
}
if (abst)
@@ -650,7 +651,8 @@ traverse_object (type& c)
os << endl;
os << strlit ((i == b ? " WHERE " : " AND ") + table + "." +
- quote_id (i->name) + "=" + qp->next ());
+ quote_id (i->name) + "=" +
+ convert_to (qp->next (), i->type, *i->member));
}
os << ";"
@@ -689,13 +691,14 @@ traverse_object (type& c)
os << endl;
os << strlit ((i == b ? " WHERE " : " AND ") +
- quote_id (i->name) + "=" + qp->next ());
+ quote_id (i->name) + "=" +
+ convert_to (qp->next (), i->type, *i->member));
}
if (optimistic != 0 && !poly_derived)
os << endl
- << strlit (" AND " + column_qname (*optimistic) +
- "=" + qp->next ());
+ << strlit (" AND " + column_qname (*optimistic) + "=" +
+ convert_to (qp->next (), *optimistic));
os << ";"
<< endl;
}
@@ -713,7 +716,8 @@ traverse_object (type& c)
os << endl
<< strlit ((i == b ? " WHERE " : " AND ") +
- quote_id (i->name) + "=" + qp->next ());
+ quote_id (i->name) + "=" +
+ convert_to (qp->next (), i->type, *i->member));
}
os << ";"
@@ -733,12 +737,13 @@ traverse_object (type& c)
{
os << endl
<< strlit ((i == b ? " WHERE " : " AND ") +
- quote_id (i->name) + "=" + qp->next ());
+ quote_id (i->name) + "=" +
+ convert_to (qp->next (), i->type, *i->member));
}
os << endl
- << strlit (" AND " + column_qname (*optimistic) +
- "=" + qp->next ()) << ";"
+ << strlit (" AND " + column_qname (*optimistic) + "=" +
+ convert_to (qp->next (), *optimistic)) << ";"
<< endl;
}
}
diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx
index de0e6bd..7ee8a06 100644
--- a/odb/relational/source.hxx
+++ b/odb/relational/source.hxx
@@ -238,6 +238,8 @@ namespace relational
r += column; // Already quoted.
+ string const& sqlt (column_type ());
+
// Version column (optimistic concurrency) requires special
// handling in the UPDATE statement.
//
@@ -249,12 +251,12 @@ namespace relational
else if (param_ != 0)
{
r += '=';
- r += param_->next ();
+ r += convert_to (param_->next (), sqlt, m);
}
+ else if (sk_ == statement_select)
+ r = convert_from (r, sqlt, m);
- sc_.push_back (
- statement_column (
- table, r, column_type (), m, key_prefix_));
+ sc_.push_back (statement_column (table, r, sqlt, m, key_prefix_));
}
protected:
@@ -434,7 +436,10 @@ namespace relational
string const& table,
string const& column)
{
- sc_.push_back (statement_column (table, column, column_type (), m));
+ string const& sqlt (column_type ());
+ sc_.push_back (
+ statement_column (
+ table, convert_from (column, sqlt, m), sqlt, m));
}
protected:
@@ -1875,7 +1880,8 @@ namespace relational
{
os << endl
<< strlit ((i == b ? " WHERE " : " AND ") + inv_table + "." +
- quote_id (i->name) + "=" + qp->next ());
+ quote_id (i->name) + "=" +
+ convert_to (qp->next (), i->type, *i->member));
}
}
else
@@ -1928,7 +1934,8 @@ namespace relational
{
os << endl
<< strlit ((i == b ? " WHERE " : " AND ") + table + "." +
- quote_id (i->name) + "=" + qp->next ());
+ quote_id (i->name) + "=" +
+ convert_to (qp->next (), i->type, *i->member));
}
if (ordered)
@@ -1995,13 +2002,13 @@ namespace relational
string values;
instance<query_parameters> qp;
- for (size_t i (0), n (m.get<size_t> ("data-column-count"));
- i < n; ++i)
+ for (statement_columns::const_iterator b (sc.begin ()), i (b),
+ e (sc.end ()); i != e; ++i)
{
- if (i != 0)
+ if (i != b)
values += ',';
- values += qp->next ();
+ values += convert_to (qp->next (), i->type, *i->member);
}
os << strlit (" VALUES (" + values + ")") << ";"
@@ -2027,7 +2034,8 @@ namespace relational
{
os << endl
<< strlit ((i == b ? " WHERE " : " AND ") +
- quote_id (i->name) + "=" + qp->next ());
+ quote_id (i->name) + "=" +
+ convert_to (qp->next (), i->type, *i->member));
}
os << ";"
@@ -2995,22 +3003,22 @@ namespace relational
// Output a list of parameters for the persist statement.
//
- struct persist_statement_params: object_members_base, virtual context
+ struct persist_statement_params: object_columns_base, virtual context
{
persist_statement_params (string& params, query_parameters& qp)
- : params_ (params), count_ (0), qp_ (qp)
+ : params_ (params), qp_ (qp)
{
}
virtual void
traverse_pointer (semantics::data_member& m, semantics::class_& c)
{
- if (!inverse (m))
- object_members_base::traverse_pointer (m, c);
+ if (!inverse (m, key_prefix_))
+ object_columns_base::traverse_pointer (m, c);
}
- virtual void
- traverse_simple (semantics::data_member& m)
+ virtual bool
+ traverse_column (semantics::data_member& m, string const&, bool first)
{
string p;
@@ -3023,16 +3031,17 @@ namespace relational
if (!p.empty ())
{
- if (count_++ != 0)
+ if (!first)
params_ += ',';
- params_ += p;
+ params_ += (p != "DEFAULT" ? convert_to (p, column_type (), m) : p);
}
+
+ return !p.empty ();
}
private:
string& params_;
- size_t count_;
query_parameters& qp_;
};
diff --git a/odb/relational/sqlite/context.cxx b/odb/relational/sqlite/context.cxx
index 3b8efb6..faaa3b5 100644
--- a/odb/relational/sqlite/context.cxx
+++ b/odb/relational/sqlite/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;
+ }
+
namespace
{
struct has_grow: traversal::class_
@@ -230,66 +237,67 @@ namespace relational
{
typedef context::invalid_sql_type invalid_sql_type;
- sql_parser (std::string const& sql)
- : l_ (sql)
- {
- }
+ sql_parser (custom_db_types const* ct): ct_ (ct) {}
sql_type
- parse ()
+ parse (string sql)
{
+ 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 (sql))
+ {
+ r.to = t.type.replace (sql, t.to);
+ r.from = t.type.replace (sql, t.from);
+ sql = t.type.replace (sql, t.as);
+ break;
+ }
+ }
+ }
+
+ // Parse the type into a sequence of identifiers.
+ //
try
{
+ l_.lex (sql);
+
for (sql_token t (l_.next ()); t.type () != sql_token::t_eos;)
{
sql_token::token_type tt (t.type ());
if (tt == sql_token::t_identifier)
{
- string const& id (context::upcase (t.identifier ()));
-
- // Column constraints start with one of the following
- // keywords. Use them to determine when to stop parsing.
- //
- if (id == "CONSTRAINT" ||
- id == "PRIMARY" ||
- id == "NOT" ||
- id == "UNIQUE" ||
- id == "CHECK" ||
- id == "DEFAULT" ||
- id == "COLLATE" ||
- id == "REFERENCES")
- {
- break;
- }
-
- ids_.push_back (id);
+ ids_.push_back (context::upcase (t.identifier ()));
t = l_.next ();
if (t.punctuation () == sql_token::p_lparen)
{
- parse_range ();
+ if (!parse_range ())
+ return error (m_);
+
t = l_.next ();
}
}
else
- {
- throw invalid_sql_type (
- "expected SQLite type name instead of '" + t.string () +
- "'");
- }
+ return error ("expected SQLite type name instead of '" +
+ t.string () + "'");
}
}
catch (sql_lexer::invalid_input const& e)
{
- throw invalid_sql_type (
- "invalid SQLite type declaration: " + e.message);
+ return error ("invalid SQLite type declaration: " + e.message);
}
if (ids_.empty ())
- throw invalid_sql_type ("expected SQLite type name");
-
- sql_type r;
+ return error ("expected SQLite type name");
// Apply the first four rules of the SQLite type to affinity
// conversion algorithm.
@@ -319,15 +327,13 @@ namespace relational
else if (id == "DATE" || id == "TIME" || id == "DATETIME")
r.type = sql_type::TEXT;
else
- {
- throw invalid_sql_type ("unknown SQLite type '" + id + "'");
- }
+ return error ("unknown SQLite type '" + id + "'");
}
return r;
}
- void
+ bool
parse_range ()
{
// Skip tokens until we get the closing paren.
@@ -339,10 +345,22 @@ namespace relational
if (t.type () == sql_token::t_eos)
{
- throw invalid_sql_type (
- "missing ')' in SQLite type declaration");
+ m_ = "missing ')' in SQLite type declaration";
+ return false;
}
}
+
+ return true;
+ }
+
+ private:
+ sql_type
+ error (string const& m)
+ {
+ if (ct_ == 0)
+ return sql_type ();
+ else
+ throw invalid_sql_type (m);
}
bool
@@ -359,29 +377,41 @@ namespace relational
}
private:
- typedef vector<string> identifiers;
-
- private:
+ custom_db_types const* ct_;
sql_lexer l_;
+ string m_; // Error message.
+
+ typedef vector<string> identifiers;
identifiers ids_;
};
}
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)
{
@@ -394,10 +424,10 @@ namespace relational
}
sql_type context::
- parse_sql_type (string const& t)
+ parse_sql_type (string const& sqlt, custom_db_types const* ct)
{
- sql_parser p (t);
- return p.parse ();
+ sql_parser p (ct);
+ return p.parse (sqlt);
}
}
}
diff --git a/odb/relational/sqlite/context.hxx b/odb/relational/sqlite/context.hxx
index 3c17d55..6da5b9b 100644
--- a/odb/relational/sqlite/context.hxx
+++ b/odb/relational/sqlite/context.hxx
@@ -29,14 +29,20 @@ namespace relational
sql_type (): type (invalid) {}
core_type type;
+
+ // 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
{
@@ -49,10 +55,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 const&, custom_db_types const* = 0);
protected:
+ virtual string const&
+ convert_expr (string const&, semantics::data_member&, bool);
+
virtual bool
grow_impl (semantics::class_&);
@@ -90,7 +103,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_;
};
diff --git a/odb/relational/sqlite/model.cxx b/odb/relational/sqlite/model.cxx
index 41d5d6c..b581ea3 100644
--- a/odb/relational/sqlite/model.cxx
+++ b/odb/relational/sqlite/model.cxx
@@ -28,7 +28,8 @@ namespace relational
{
// Make sure the column is mapped to INTEGER.
//
- if (parse_sql_type (column_type (), m).type != sql_type::INTEGER)
+ sql_type const& t (parse_sql_type (column_type (), m, false));
+ if (t.type != sql_type::INTEGER)
{
cerr << m.file () << ":" << m.line () << ":" << m.column ()
<< ": error: column with default value specified as C++ "
diff --git a/odb/relational/validator.cxx b/odb/relational/validator.cxx
new file mode 100644
index 0000000..ec78e8f
--- /dev/null
+++ b/odb/relational/validator.cxx
@@ -0,0 +1,99 @@
+// file : odb/relational/validator.cxx
+// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <iostream>
+
+#include <odb/diagnostics.hxx>
+#include <odb/traversal.hxx>
+#include <odb/relational/common.hxx>
+#include <odb/relational/context.hxx>
+#include <odb/relational/validator.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ void validator::
+ validate (options const&,
+ features&,
+ semantics::unit& u,
+ semantics::path const&,
+ unsigned short pass)
+ {
+ bool valid (true);
+
+ // Validate custom type mapping.
+ //
+ if (pass == 1)
+ {
+ // Create an empty list if we don't have one. This makes the
+ // rest of the code simpler.
+ //
+ if (!u.count ("custom-db-types"))
+ u.set ("custom-db-types", custom_db_types ());
+
+ custom_db_types & cts (u.get<custom_db_types> ("custom-db-types"));
+
+ for (custom_db_types::iterator i (cts.begin ()); i != cts.end (); ++i)
+ {
+ custom_db_type& ct (*i);
+
+ if (ct.type.empty ())
+ {
+ error (ct.loc) << "'type' clause expected in db pragma map" << endl;
+ valid = false;
+ }
+
+ if (ct.as.empty ())
+ {
+ error (ct.loc) << "'as' clause expected in db pragma map" << endl;
+ valid = false;
+ }
+
+ if (ct.to.empty ())
+ ct.to = "(?)";
+ else
+ {
+ size_t p (ct.to.find ("(?)"));
+
+ if (p == string::npos)
+ {
+ error (ct.loc) << "no '(?)' expression in the 'to' clause "
+ << "of db pragma map" << endl;
+ valid = false;
+ }
+ else if (ct.to.find ("(?)", p + 3) != string::npos)
+ {
+ error (ct.loc) << "multiple '(?)' expressions in the 'to' "
+ << "clause of db pragma map" << endl;
+ valid = false;
+ }
+ }
+
+ if (ct.from.empty ())
+ ct.from = "(?)";
+ else
+ {
+ size_t p (ct.from.find ("(?)"));
+
+ if (p == string::npos)
+ {
+ error (ct.loc) << "no '(?)' expression in the 'from' clause "
+ << "of db pragma map" << endl;
+ valid = false;
+ }
+ else if (ct.from.find ("(?)", p + 3) != string::npos)
+ {
+ error (ct.loc) << "multiple '(?)' expressions in the 'from' "
+ << "clause of db pragma map" << endl;
+ valid = false;
+ }
+ }
+ }
+ }
+
+ if (!valid)
+ throw failed ();
+ }
+}
diff --git a/odb/relational/validator.hxx b/odb/relational/validator.hxx
new file mode 100644
index 0000000..485602d
--- /dev/null
+++ b/odb/relational/validator.hxx
@@ -0,0 +1,36 @@
+// file : odb/relational/validator.hxx
+// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_VALIDATOR_HXX
+#define ODB_RELATIONAL_VALIDATOR_HXX
+
+#include <odb/options.hxx>
+#include <odb/features.hxx>
+#include <odb/semantics/unit.hxx>
+
+namespace relational
+{
+ class validator
+ {
+ public:
+ struct failed {};
+
+ // The first pass is performed before processing. The second -- after.
+ //
+ void
+ validate (options const&,
+ features&,
+ semantics::unit&,
+ semantics::path const&,
+ unsigned short pass);
+
+ validator () {}
+
+ private:
+ validator (validator const&);
+ validator& operator= (validator const&);
+ };
+}
+
+#endif // ODB_RELATIONAL_VALIDATOR_HXX
diff --git a/odb/validator.cxx b/odb/validator.cxx
index b0a6492..597da01 100644
--- a/odb/validator.cxx
+++ b/odb/validator.cxx
@@ -11,6 +11,8 @@
#include <odb/context.hxx>
#include <odb/validator.hxx>
+#include <odb/relational/validator.hxx>
+
using namespace std;
namespace
@@ -1230,61 +1232,73 @@ void validator::
validate (options const& ops,
features& f,
semantics::unit& u,
- semantics::path const&,
+ semantics::path const& p,
unsigned short pass)
{
- auto_ptr<context> ctx (create_context (cerr, u, ops, f, 0));
-
bool valid (true);
- if (pass == 1)
- {
- traversal::unit unit;
- traversal::defines unit_defines;
- traversal::declares unit_declares;
- typedefs1 unit_typedefs (unit_declares);
- traversal::namespace_ ns;
- value_type vt (valid);
- class1 c (valid, vt);
-
- unit >> unit_defines >> ns;
- unit_defines >> c;
- unit >> unit_declares >> vt;
- unit >> unit_typedefs >> c;
-
- traversal::defines ns_defines;
- traversal::declares ns_declares;
- typedefs1 ns_typedefs (ns_declares);
-
- ns >> ns_defines >> ns;
- ns_defines >> c;
- ns >> ns_declares >> vt;
- ns >> ns_typedefs >> c;
-
- unit.dispatch (u);
- }
- else
{
- traversal::unit unit;
- traversal::defines unit_defines;
- typedefs unit_typedefs (true);
- traversal::namespace_ ns;
- class2 c (valid);
+ auto_ptr<context> ctx (create_context (cerr, u, ops, f, 0));
+
+ if (pass == 1)
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ traversal::declares unit_declares;
+ typedefs1 unit_typedefs (unit_declares);
+ traversal::namespace_ ns;
+ value_type vt (valid);
+ class1 c (valid, vt);
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_declares >> vt;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ traversal::declares ns_declares;
+ typedefs1 ns_typedefs (ns_declares);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_declares >> vt;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (u);
+ }
+ else
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (true);
+ traversal::namespace_ ns;
+ class2 c (valid);
- unit >> unit_defines >> ns;
- unit_defines >> c;
- unit >> unit_typedefs >> c;
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
- traversal::defines ns_defines;
- typedefs ns_typedefs (true);
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (true);
- ns >> ns_defines >> ns;
- ns_defines >> c;
- ns >> ns_typedefs >> c;
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
- unit.dispatch (u);
+ unit.dispatch (u);
+ }
}
if (!valid)
throw failed ();
+
+ try
+ {
+ relational::validator v;
+ v.validate (ops, f, u, p, pass);
+ }
+ catch (relational::validator::failed const&)
+ {
+ throw failed ();
+ }
}