aboutsummaryrefslogtreecommitdiff
path: root/odb/relational/pgsql
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/pgsql
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/pgsql')
-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
4 files changed, 131 insertions, 44 deletions
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));
}
}