aboutsummaryrefslogtreecommitdiff
path: root/odb/relational/mssql
diff options
context:
space:
mode:
Diffstat (limited to 'odb/relational/mssql')
-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
5 files changed, 176 insertions, 64 deletions
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