aboutsummaryrefslogtreecommitdiff
path: root/odb/relational/oracle
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2012-07-10 15:17:16 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2012-07-10 15:17:16 +0200
commitb8554760aa3a5c5697c77d11e507a2bb46dbf8e4 (patch)
tree3f2bcb28a59eb0d4cce4586acec4a8c639cde7e6 /odb/relational/oracle
parent1b64460a2b2c5411b6052cd4c4d8e8b0d46a4086 (diff)
Add support for custom database type mapping
New pragma qualifier, map, and specifiers: as, to, from. New tests: <database>/custom.
Diffstat (limited to 'odb/relational/oracle')
-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
5 files changed, 132 insertions, 64 deletions
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 ());
}