From b8554760aa3a5c5697c77d11e507a2bb46dbf8e4 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 10 Jul 2012 15:17:16 +0200 Subject: Add support for custom database type mapping New pragma qualifier, map, and specifiers: as, to, from. New tests: /custom. --- odb/context.hxx | 1 + odb/generator.cxx | 2 +- odb/makefile | 3 +- odb/pragma.cxx | 338 +++++++++++++++++++++++++++----------- odb/pragma.hxx | 9 +- odb/relational/common.cxx | 7 +- odb/relational/context.cxx | 16 ++ odb/relational/context.hxx | 65 ++++++++ odb/relational/mssql/common.cxx | 3 +- odb/relational/mssql/context.cxx | 181 +++++++++++++------- odb/relational/mssql/context.hxx | 48 +++++- odb/relational/mssql/model.cxx | 2 +- odb/relational/mssql/source.cxx | 6 +- odb/relational/mysql/context.cxx | 95 ++++++++--- odb/relational/mysql/context.hxx | 49 +++++- odb/relational/mysql/model.cxx | 2 +- odb/relational/mysql/source.cxx | 41 ++--- odb/relational/oracle/common.cxx | 3 +- odb/relational/oracle/context.cxx | 139 +++++++++------- odb/relational/oracle/context.hxx | 49 +++++- odb/relational/oracle/model.cxx | 3 +- odb/relational/oracle/source.cxx | 2 +- odb/relational/pgsql/context.cxx | 115 +++++++++---- odb/relational/pgsql/context.hxx | 55 ++++++- odb/relational/pgsql/model.cxx | 2 +- odb/relational/pgsql/source.cxx | 3 +- odb/relational/source.cxx | 23 ++- odb/relational/source.hxx | 51 +++--- odb/relational/sqlite/context.cxx | 134 +++++++++------ odb/relational/sqlite/context.hxx | 49 +++++- odb/relational/sqlite/model.cxx | 3 +- odb/relational/validator.cxx | 99 +++++++++++ odb/relational/validator.hxx | 36 ++++ odb/validator.cxx | 102 +++++++----- 34 files changed, 1269 insertions(+), 467 deletions(-) create mode 100644 odb/relational/validator.cxx create mode 100644 odb/relational/validator.hxx (limited to 'odb') 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 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 #include #include + #include +#include 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; tt = pragma_lex (&t); } + else if (qualifier == "map" && + (p == "type" || + p == "as" || + p == "to" || + p == "from")) + { + // type("") + // as("") + // to("") + // from("") + // + 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 ()); + + 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("") as("") [to("")] [from("")] + // + 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; + tt = pragma_lex (&t); + } + else if (p == "namespace") { // namespace [()] // 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 { typedef std::set base; - void + pragma& insert (pragma const& p) { std::pair r (base::insert (p)); + pragma& x (const_cast (*r.first)); + if (!r.second) - { - pragma& x (const_cast (*r.first)); x = p; - } + + return x; } template 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_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") : 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 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 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") : 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 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 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 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") : 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 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 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") : 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 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 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 qp; - for (size_t i (0), n (m.get ("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 identifiers; - - private: + custom_db_types const* ct_; sql_lexer l_; + string m_; // Error message. + + typedef vector 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") : 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 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 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 + +#include +#include +#include +#include +#include + +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")); + + 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 +#include +#include + +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 #include +#include + 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 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 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 (); + } } -- cgit v1.1