From 923639283d2bae0b82cb605fa4680a3058c9d973 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 25 Jul 2012 12:13:38 +0200 Subject: Add support for defining indexes New db pragma qualifier: index. New tests: common/index, mysql/index, pgsql/index. --- odb/cxx-lexer.cxx | 4 +- odb/cxx-lexer.hxx | 2 +- odb/diagnostics.hxx | 20 ++ odb/location.cxx | 14 + odb/location.hxx | 28 ++ odb/lookup.cxx | 27 +- odb/lookup.hxx | 6 + odb/makefile | 1 + odb/pragma.cxx | 599 +++++++++++++++++++++++++++++++------ odb/relational/context.hxx | 35 +++ odb/relational/model.cxx | 39 +-- odb/relational/model.hxx | 216 ++++++++++--- odb/relational/mysql/schema.cxx | 30 ++ odb/relational/pgsql/schema.cxx | 51 +++- odb/relational/processor.cxx | 260 +++++++++++++++- odb/relational/schema.hxx | 27 +- odb/relational/source.cxx | 2 +- odb/relational/validator.cxx | 118 ++++++++ odb/semantics/elements.cxx | 4 +- odb/semantics/elements.hxx | 19 +- odb/semantics/relational/index.hxx | 31 +- odb/semantics/relational/key.hxx | 9 + odb/validator.cxx | 87 +++--- 23 files changed, 1391 insertions(+), 238 deletions(-) create mode 100644 odb/location.cxx create mode 100644 odb/location.hxx diff --git a/odb/cxx-lexer.cxx b/odb/cxx-lexer.cxx index b02b4e0..dc09a17 100644 --- a/odb/cxx-lexer.cxx +++ b/odb/cxx-lexer.cxx @@ -37,11 +37,11 @@ cxx_lexer:: // void cxx_tokens_lexer:: -start (cxx_tokens const& ts) +start (cxx_tokens const& ts, location_t start_loc) { tokens_ = &ts; cur_ = ts.begin (); - loc_ = 0; + loc_ = start_loc; } cpp_ttype cxx_tokens_lexer:: diff --git a/odb/cxx-lexer.hxx b/odb/cxx-lexer.hxx index 9235ecf..2d0ca06 100644 --- a/odb/cxx-lexer.hxx +++ b/odb/cxx-lexer.hxx @@ -47,7 +47,7 @@ class cxx_tokens_lexer: public cxx_lexer { public: void - start (cxx_tokens const&); + start (cxx_tokens const&, location_t start_loc = 0); virtual cpp_ttype next (std::string& token, tree* node = 0); diff --git a/odb/diagnostics.hxx b/odb/diagnostics.hxx index a7dd575..b1e89b0 100644 --- a/odb/diagnostics.hxx +++ b/odb/diagnostics.hxx @@ -12,6 +12,8 @@ #include +#include + using std::endl; std::ostream& @@ -23,6 +25,24 @@ warn (cutl::fs::path const&, std::size_t line, std::size_t clmn); std::ostream& info (cutl::fs::path const&, std::size_t line, std::size_t clmn); +inline std::ostream& +error (location const& l) +{ + return error (l.file, l.line, l.column); +} + +inline std::ostream& +warn (location const&l) +{ + return warn (l.file, l.line, l.column); +} + +inline std::ostream& +info (location const&l) +{ + return info (l.file, l.line, l.column); +} + std::ostream& error (location_t); diff --git a/odb/location.cxx b/odb/location.cxx new file mode 100644 index 0000000..ba8d9a7 --- /dev/null +++ b/odb/location.cxx @@ -0,0 +1,14 @@ +// file : odb/location.cxx +// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include +#include + +location:: +location (location_t l) + : file (location_file (l)), + line (location_line (l)), + column (location_column (l)) +{ +} diff --git a/odb/location.hxx b/odb/location.hxx new file mode 100644 index 0000000..a9cc6ad --- /dev/null +++ b/odb/location.hxx @@ -0,0 +1,28 @@ +// file : odb/location.hxx +// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_LOCATION_HXX +#define ODB_LOCATION_HXX + +#include + +#include +#include + +struct location +{ + explicit + location (location_t); + + location (cutl::fs::path const& f, std::size_t l, std::size_t c) + : file (f), line (l), column (c) + { + } + + cutl::fs::path file; + std::size_t line; + std::size_t column; +}; + +#endif // ODB_LOCATION_HXX diff --git a/odb/lookup.cxx b/odb/lookup.cxx index c6b40b1..b3e0551 100644 --- a/odb/lookup.cxx +++ b/odb/lookup.cxx @@ -48,6 +48,7 @@ namespace lookup tree scope, string& name, bool is_type, + bool trailing_scope, tree* end_scope) { tree id; @@ -78,7 +79,24 @@ namespace lookup ptt = tt; tt = l.next (tl, &tn); - bool last (tt != CPP_SCOPE); + bool last (true); + if (tt == CPP_SCOPE) + { + // If trailing scope names are allowed, then we also need to + // check what's after the scope. + // + if (trailing_scope) + { + ptt = tt; + tt = l.next (tl, &tn); + + if (tt == CPP_NAME) + last = false; + } + else + last = false; + } + tree decl = lookup_qualified_name (scope, id, last && is_type, false); // If this is the first component in the name, then also search the @@ -110,8 +128,11 @@ namespace lookup name += "::"; - ptt = tt; - tt = l.next (tl, &tn); + if (!trailing_scope) + { + ptt = tt; + tt = l.next (tl, &tn); + } } return scope; diff --git a/odb/lookup.hxx b/odb/lookup.hxx index 7df8f34..c207543 100644 --- a/odb/lookup.hxx +++ b/odb/lookup.hxx @@ -40,6 +40,11 @@ namespace lookup std::string& tl, // Token literal. tree& tn); // Token node. + // If trailing_scope is true, then this function also handles + // names in the 'foo::bar::' form. + // In this case token will be and + // ptt will be CPP_SCOPE. + // tree resolve_scoped_name (cxx_lexer&, cpp_ttype&, @@ -49,6 +54,7 @@ namespace lookup tree start_scope, std::string& name, bool is_type, + bool trailing_scope = false, tree* end_scope = 0); } diff --git a/odb/makefile b/odb/makefile index d906fa0..c57ed9f 100644 --- a/odb/makefile +++ b/odb/makefile @@ -12,6 +12,7 @@ sql-token.cxx \ sql-lexer.cxx \ context.cxx \ common.cxx \ +location.cxx \ diagnostics.cxx \ emitter.cxx \ lookup.cxx \ diff --git a/odb/pragma.cxx b/odb/pragma.cxx index 0d21f7b..abb6692 100644 --- a/odb/pragma.cxx +++ b/odb/pragma.cxx @@ -319,14 +319,21 @@ resolve_scoped_name (cxx_lexer& l, tree& tn, string& name, bool is_type, - string const& prag) + string const& prag, + bool trailing_scope = false, + cpp_ttype* prev_tt = 0) { try { cpp_ttype ptt; // Not used. - return + tree r ( lookup::resolve_scoped_name ( - l, tt, tl, tn, ptt, current_scope (), name, is_type); + l, tt, tl, tn, ptt, current_scope (), name, is_type, trailing_scope)); + + if (prev_tt != 0) + *prev_tt = ptt; + + return r; } catch (lookup::invalid_name const&) { @@ -365,11 +372,13 @@ check_spec_decl_type (tree d, return false; } } - else if (p == "auto" || - p == "column" || - p == "inverse" || + else if (p == "auto" || + p == "column" || + p == "inverse" || p == "transient" || - p == "version") + p == "version" || + p == "index" || + p == "unique") { if (tc != FIELD_DECL) { @@ -580,7 +589,278 @@ handle_pragma (cxx_lexer& l, pragma::add_func adder (0); // Custom context adder. location_t loc (l.location ()); // Pragma location. - if (p == "table") + if (qualifier == "map") + { + // type("") + // as("") + // to("") + // from("") + // + + if (p != "type" && + p != "as" && + p != "to" && + p != "from") + { + error (l) << "unknown db pragma " << p << endl; + return; + } + + 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 (l.next (tl, &tn) != CPP_OPEN_PAREN) + { + error (l) << "'(' expected after db pragma " << p << endl; + return; + } + + tt = l.next (tl, &tn); + + if (p == "type") + { + if (tt != CPP_STRING) + { + error (l) << "type name regex expected in db pragma " << p << endl; + return; + } + + try + { + // Make it case-insensitive. + // + ct.type.assign (tl, true); + } + catch (regex_format const& e) + { + error (l) << "invalid regex: '" << e.regex () << "' in db pragma " + << p << ": " << e.description () << endl; + return; + } + } + else if (p == "as") + { + if (tt != CPP_STRING) + { + error (l) << "type name expected in db pragma " << p << endl; + return; + } + + ct.as = tl; + } + else if (p == "to") + { + if (tt != CPP_STRING) + { + error (l) << "expression expected in db pragma " << p << endl; + return; + } + + ct.to = tl; + } + else if (p == "from") + { + if (tt != CPP_STRING) + { + error (l) << "expression expected in db pragma " << p << endl; + return; + } + + ct.from = tl; + } + + if (l.next (tl, &tn) != CPP_CLOSE_PAREN) + { + error (l) << "')' expected at the end of db pragma " << p << endl; + return; + } + + name.clear (); // We don't need to add anything for this pragma. + tt = l.next (tl, &tn); + } + else if (qualifier == "index") + { + // unique + // type("") + // method("") + // options("") + // member([, ""]) + // members([, ...]) + // + + if (p != "unique" && + p != "type" && + p != "method" && + p != "options" && + p != "member" && + p != "members") + { + error (l) << "unknown db pragma " << p << endl; + return; + } + + using relational::index; + index& in (qualifier_value.value ()); + + if (p == "unique") + in.type = "UNIQUE"; + else + { + if (l.next (tl, &tn) != CPP_OPEN_PAREN) + { + error (l) << "'(' expected after db pragma " << p << endl; + return; + } + + tt = l.next (tl, &tn); + + if (p == "type") + { + if (tt != CPP_STRING) + { + error (l) << "index type expected in db pragma " << p << endl; + return; + } + + in.type = tl; + tt = l.next (tl, &tn); + } + else if (p == "method") + { + if (tt != CPP_STRING) + { + error (l) << "index method expected in db pragma " << p << endl; + return; + } + + in.method = tl; + tt = l.next (tl, &tn); + } + else if (p == "options") + { + if (tt != CPP_STRING) + { + error (l) << "index options expected in db pragma " << p << endl; + return; + } + + in.options = tl; + tt = l.next (tl, &tn); + } + else if (p == "member") + { + if (tt != CPP_NAME) + { + error (l) << "data member name expected in db pragma " << p << endl; + return; + } + + index::member m; + m.loc = loc; + m.name = tl; + + tt = l.next (tl, &tn); + + // Parse nested members if any. + // + for (; tt == CPP_DOT; tt = l.next (tl, &tn)) + { + if (l.next (tl, &tn) != CPP_NAME) + { + error (l) << "name expected after '.' in db pragma " << p << endl; + return; + } + + m.name += '.'; + m.name += tl; + } + + // Parse member options, if any. + // + if (tt == CPP_COMMA) + { + if (l.next (tl, &tn) != CPP_STRING) + { + error (l) << "index member options expected in db pragma " << p + << endl; + return; + } + + m.options = tl; + tt = l.next (tl, &tn); + } + + in.members.push_back (m); + } + else if (p == "members") + { + for (;;) + { + if (tt != CPP_NAME) + { + error (l) << "data member name expected in db pragma " << p + << endl; + return; + } + + index::member m; + m.loc = l.location (); + m.name = tl; + + tt = l.next (tl, &tn); + + // Parse nested members if any. + // + for (; tt == CPP_DOT; tt = l.next (tl, &tn)) + { + if (l.next (tl, &tn) != CPP_NAME) + { + error (l) << "name expected after '.' in db pragma " << p + << endl; + return; + } + + m.name += '.'; + m.name += tl; + } + + in.members.push_back (m); + + if (tt == CPP_COMMA) + tt = l.next (tl, &tn); + else + break; + } + } + + if (tt != CPP_CLOSE_PAREN) + { + error (l) << "')' expected at the end of db pragma " << p << endl; + return; + } + } + + name.clear (); // We don't need to add anything for this pragma. + tt = l.next (tl, &tn); + } + else if (p == "index" || + p == "unique") + { + // index + // unique + + // Make sure we've got the correct declaration type. + // + if (decl != 0 && !check_spec_decl_type (decl, decl_name, p, loc)) + return; + + tt = l.next (tl, &tn); + } + else if (p == "table") { // table () // table ( [= ""] [: ""] (view only) @@ -1358,93 +1638,6 @@ handle_pragma (cxx_lexer& l, adder = &accumulate; tt = l.next (tl, &tn); } - 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 (l.next (tl, &tn) != CPP_OPEN_PAREN) - { - error (l) << "'(' expected after db pragma " << p << endl; - return; - } - - tt = l.next (tl, &tn); - - if (p == "type") - { - if (tt != CPP_STRING) - { - error (l) << "type name regex expected in db pragma " << p << endl; - return; - } - - try - { - // Make it case-insensitive. - // - ct.type.assign (tl, true); - } - catch (regex_format const& e) - { - error (l) << "invalid regex: '" << e.regex () << "' in db pragma " - << p << ": " << e.description () << endl; - return; - } - } - else if (p == "as") - { - if (tt != CPP_STRING) - { - error (l) << "type name expected in db pragma " << p << endl; - return; - } - - ct.as = tl; - } - else if (p == "to") - { - if (tt != CPP_STRING) - { - error (l) << "expression expected in db pragma " << p << endl; - return; - } - - ct.to = tl; - } - else if (p == "from") - { - if (tt != CPP_STRING) - { - error (l) << "expression expected in db pragma " << p << endl; - return; - } - - ct.from = tl; - } - - if (l.next (tl, &tn) != CPP_CLOSE_PAREN) - { - error (l) << "')' expected at the end of db pragma " << p << endl; - return; - } - - name.clear (); // We don't need to add anything for this pragma. - tt = l.next (tl, &tn); - } else if (p == "type" || p == "id_type" || p == "value_type" || @@ -1777,6 +1970,26 @@ check_qual_decl_type (tree d, { assert (d == global_namespace); } + else if (p == "index") + { + if (tc != RECORD_TYPE) + { + // For an index, name is not empty only if the class name was + // specified explicitly. Otherwise, the index definition scope + // is assumed. + // + if (name.empty ()) + { + error (l) << "db pragma " << p << " outside of a class scope" << endl; + info (l) << "use the db pragma " << p << "() syntax " + << " instead" << endl; + } + else + error (l) << "name '" << name << "' in db pragma " << p << " does " + << "not refer to a class" << endl; + return false; + } + } else if (p == "namespace") { if (tc != NAMESPACE_DECL) @@ -1856,6 +2069,8 @@ handle_pragma_qualifier (cxx_lexer& l, string const& p) pragma::add_func adder (0); // Custom context adder. bool ns (false); // Namespace location pragma. + cxx_tokens saved_tokens; // Saved token seuqnece to be replayed. + // Pragma qualifiers. // if (p == "map") @@ -1872,6 +2087,175 @@ handle_pragma_qualifier (cxx_lexer& l, string const& p) adder = &accumulate; tt = l.next (tl, &tn); } + else if (p == "index") + { + // Index can be both a qualifier and a specifier. Things are complicated + // by the fact that when it is a specifier, it belongs to a member which + // means that the actual qualifier ('member') can be omitted. So we need + // to distinguish between cases like these: + // + // #pragma db index type("INTEGER") // specifier + // #pragma db index type("UNIQUE") member(foo_) // qualifier + // + // The thing that determines whether this is a qualifier or a specifier + // is the presence of the 'member' or 'members' specifier. So to handle + // this we are going to pre-scan the pragma looking for 'member' or + // 'members' and saving the tokens. Once we determine what this is, + // we replay the saved tokens to actually parse them. + // + tt = l.next (tl, &tn); + + if (tt != CPP_OPEN_PAREN) + { + // Determine what this is by scanning the pragma until we see + // the 'member' qualifier or EOF. + // + bool qual (false); + size_t balance (0); + + for (; tt != CPP_EOF; tt = l.next (tl, &tn)) + { + switch (tt) + { + case CPP_OPEN_PAREN: + { + balance++; + break; + } + case CPP_CLOSE_PAREN: + { + if (balance > 0) + balance--; + else + { + error (l) << "unbalanced parenthesis in db pragma " << p << endl; + return; + } + break; + } + case CPP_NAME: + { + if (balance == 0 && (tl == "member" || tl == "members")) + qual = true; + break; + } + default: + break; + } + + if (qual) + break; + + cxx_token ct (l.location (), tt); + ct.literal = tl; + ct.node = tn; + saved_tokens.push_back (ct); + } + + if (balance != 0) + { + error (l) << "unbalanced parenthesis in db pragma " << p << endl; + return; + } + + if (qual) + { + // This is a qualifer. The saved tokens sequence contains tokens + // until the first 'member' or 'members' specifier. So we will + // first need to re-play these tokens and then continue parsing + // as if we just saw the 'member' or 'members' specifier. The + // token type (tt) and token literal (tl) variables should contain + // the correct values. + // + orig_decl = decl = current_scope (); + } + else + { + // This is a specifier. The saved tokens sequence contains all the + // tokens in this pragma until EOF. + // + cxx_tokens_lexer l; + l.start (saved_tokens, loc); + handle_pragma (l, "index", "member", val, 0, "", false); + return; + } + } + + relational::index in; + in.loc = loc; + + if (tt == CPP_OPEN_PAREN) + { + // Specifier with the class fq-name, index name, or both. + // + // index() + // index("") + // index(::"") + // + tt = l.next (tl, &tn); + + // Resolve class name, if any. + // + if (tt == CPP_NAME || tt == CPP_SCOPE) + { + cpp_ttype ptt; + orig_decl = resolve_scoped_name ( + l, tt, tl, tn, decl_name, true, p, true, &ptt); + + if (orig_decl == 0) + return; // Diagnostics has already been issued. + + // Get the actual type if this is a TYPE_DECL. Also get the main + // variant. + // + if (TREE_CODE (orig_decl) == TYPE_DECL) + orig_decl = TREE_TYPE (orig_decl); + + if (TYPE_P (orig_decl)) // Can be a template. + decl = TYPE_MAIN_VARIANT (orig_decl); + else + decl = orig_decl; + + if (tt == CPP_STRING && ptt != CPP_SCOPE) + { + error (l) << "'::' expected before index name in db pragma " << p + << endl; + return; + } + + if (tt != CPP_STRING && ptt == CPP_SCOPE) + { + error (l) << "index name expected after '::' in db pragma " << p + << endl; + return; + } + } + else + orig_decl = decl = current_scope (); + + // Make sure we've got the correct declaration type. + // + if (!check_qual_decl_type (decl, decl_name, p, loc)) + return; + + if (tt == CPP_STRING) + { + in.name = tl; + tt = l.next (tl, &tn); + } + + if (tt != CPP_CLOSE_PAREN) + { + error (l) << "')' expected at the end of db pragma " << p << endl; + return; + } + + tt = l.next (tl, &tn); + } + + val = in; + adder = &accumulate; + } else if (p == "namespace") { // namespace [()] @@ -2032,6 +2416,7 @@ handle_pragma_qualifier (cxx_lexer& l, string const& p) // else if (p == "id" || p == "auto" || + p == "unique" || p == "column" || p == "value_column" || p == "index_column" || @@ -2104,8 +2489,30 @@ handle_pragma_qualifier (cxx_lexer& l, string const& p) } } - // See if there are any more pragmas. + // See if there are any saved tokens to replay. // + if (!saved_tokens.empty ()) + { + cxx_tokens_lexer l; + l.start (saved_tokens); + + string tl; + cpp_ttype tt (l.next (tl)); + + if (tt == CPP_NAME || tt == CPP_KEYWORD) + { + handle_pragma (l, tl, p, *pval, decl, decl_name, ns); + + if (errorcount != 0) // Avoid parsing the rest if there was an error. + return; + } + else if (tt != CPP_EOF) + { + error (l) << "unexpected text after " << p << " in db pragma" << endl; + return; + } + } + if (tt == CPP_NAME || tt == CPP_KEYWORD) { handle_pragma (l, tl, p, *pval, decl, decl_name, ns); diff --git a/odb/relational/context.hxx b/odb/relational/context.hxx index ca55650..990e0aa 100644 --- a/odb/relational/context.hxx +++ b/odb/relational/context.hxx @@ -23,6 +23,41 @@ namespace relational statement_where // WHERE clause. }; + // Index. + // + struct index + { + location_t loc; // Location of this index definition. + std::string name; // If empty, then derive from the member name. + std::string type; // E.g., "UNIQUE", etc. + std::string method; // E.g., "BTREE", etc. + std::string options; // Database-specific index options. + + struct member + { + location_t loc; // Location of this member specifier. + std::string name; // Member name, e.g., foo_, foo_.bar_. + data_member_path path; // Member path. + std::string options; // Member options, e.g., "ASC", etc. + }; + typedef std::vector members_type; + + members_type members; + }; + + typedef std::vector indexes; + + // Indexes in the above vector are in location order. + // + struct index_comparator + { + bool + operator() (index const& x, index const& y) const + { + return x.loc < y.loc; + } + }; + // Custom database type mapping. // struct custom_db_type diff --git a/odb/relational/model.cxx b/odb/relational/model.cxx index bd91b3c..48c9bd2 100644 --- a/odb/relational/model.cxx +++ b/odb/relational/model.cxx @@ -8,6 +8,8 @@ #include #include +#include + #include #include @@ -143,24 +145,25 @@ namespace relational } catch (sema_rel::duplicate_name const& e) { - semantics::node& o (*e.orig.get ("cxx-node")); - semantics::node& d (*e.dup.get ("cxx-node")); - - cerr << d.file () << ":" << d.line () << ":" << d.column () - << ": error: " << e.dup.kind () << " name '" << e.name - << "' conflicts with an already defined " << e.orig.kind () - << " name" - << endl; - - cerr << o.file () << ":" << o.line () << ":" << o.column () - << ": info: conflicting " << e.orig.kind () << " is " - << "defined here" - << endl; - - cerr << d.file () << ":" << d.line () << ":" << d.column () - << ": error: use '#pragma db column' or '#pragma db table' " - << "to change one of the names" - << endl; + location const& o (e.orig.get ("cxx-location")); + location const& d (e.dup.get ("cxx-location")); + + error (d) << e.dup.kind () << " name '" << e.name << "' conflicts " + << "with an already defined " << e.orig.kind () << " name" + << endl; + + info (o) << "conflicting " << e.orig.kind () << " is defined here" + << endl; + + if (e.dup.kind () == "index") + error (d) << "use #pragma db index to change one of the names" + << endl; + else if (e.dup.kind () == "table") + error (d) << "use #pragma db table to change one of the names" + << endl; + else + error (d) << "use #pragma db column to change its name" + << endl; throw operation_failed (); } diff --git a/odb/relational/model.hxx b/odb/relational/model.hxx index b95425e..6ffffca 100644 --- a/odb/relational/model.hxx +++ b/odb/relational/model.hxx @@ -109,9 +109,7 @@ namespace relational sema_rel::column& c ( model_.new_node (col_id, column_type (), null)); - - c.set ("cxx-node", static_cast (&m)); - + c.set ("cxx-location", m.location ()); model_.new_edge (table_, c, name); // An id member cannot have a default value. @@ -196,7 +194,7 @@ namespace relational { pkey_ = &model_.new_node ( m.count ("auto")); - pkey_->set ("cxx-node", static_cast (idm)); + pkey_->set ("cxx-location", idm->location ()); // In most databases the primary key constraint can be // manipulated without an explicit name. So we use the special @@ -236,7 +234,7 @@ namespace relational model_.new_node ( id, table_name (c), deferred, on_delete)); - fk.set ("cxx-node", static_cast (&m)); + fk.set ("cxx-location", m.location ()); bool simple; @@ -328,6 +326,93 @@ namespace relational bool id_override_; }; + struct object_indexes: traversal::class_, virtual context + { + typedef object_indexes base; + + object_indexes (sema_rel::model& model, sema_rel::table& table) + : model_ (model), table_ (table) + { + *this >> inherits_ >> *this; + } + + object_indexes (object_indexes const& x) + : root_context (), context (), //@@ -Wextra + model_ (x.model_), table_ (x.table_) + { + *this >> inherits_ >> *this; + } + + virtual void + traverse (type& c) + { + if (!object (c)) // Ignore transient bases. + return; + + // Polymorphic bases get their own tables. + // + if (!polymorphic (c)) + inherits (c); + + indexes& ins (c.get ("index")); + + for (indexes::iterator i (ins.begin ()); i != ins.end (); ++i) + { + // Using index name as its id. + // + sema_rel::index& in ( + model_.new_node ( + i->name, i->type, i->method, i->options)); + in.set ("cxx-location", location (i->loc)); + model_.new_edge (table_, in, i->name); + + for (index::members_type::iterator j (i->members.begin ()); + j != i->members.end (); ++j) + { + using sema_rel::column; + + index::member& im (*j); + + if (type* comp = composite_wrapper (utype (*im.path.back ()))) + { + // Composite value. Get the list of the columns. Here + // column_name() returns the column prefix. + // + instance ocl (column_name (im.path)); + ocl->traverse (*comp); + + for (object_columns_list::iterator i (ocl->begin ()); + i != ocl->end (); ++i) + { + column& c ( + dynamic_cast ( + table_.find (i->name)->nameable ())); + + model_.new_edge (in, c, im.options); + } + } + else + { + // Simple value. Get the column name and look it up in the + // table. + // + column& c ( + dynamic_cast ( + table_.find (column_name (im.path))->nameable ())); + + model_.new_edge (in, c, im.options); + } + } + } + } + + private: + sema_rel::model& model_; + sema_rel::table& table_; + + traversal::inherits inherits_; + }; + struct member_create: object_members_base, virtual context { typedef member_create base; @@ -384,7 +469,6 @@ namespace relational using semantics::type; using semantics::data_member; - using sema_rel::index; using sema_rel::column; // Ignore inverse containers of object pointers. @@ -404,8 +488,7 @@ namespace relational sema_rel::container_table& t ( model_.new_node (id)); - t.set ("cxx-node", static_cast (&m)); - + t.set ("cxx-location", m.location ()); model_.new_edge (model_, t, name); // object_id @@ -418,16 +501,31 @@ namespace relational // Foreign key and index for the object id. // { + // Derive the name prefix. See the comment for the other foreign + // key code above. + // + // Note also that id_name can be a column prefix (if id is + // composite), in which case it can be empty and if not, then + // it will most likely already contain a trailing underscore. + // + string id_name (column_name (m, "id", "object_id")); + + if (id_name.empty ()) + id_name = "object_id"; + + if (id_name[id_name.size () - 1] != '_') + id_name += '_'; + + // Foreign key. + // sema_rel::foreign_key& fk ( model_.new_node ( id + ".id", table_name (*context::top_object), false, // immediate sema_rel::foreign_key::cascade)); - fk.set ("cxx-node", static_cast (&m)); - - index& in (model_.new_node (id + ".id")); - in.set ("cxx-node", static_cast (&m)); + fk.set ("cxx-location", m.location ()); + model_.new_edge (t, fk, id_name + "fk"); // Get referenced columns. // @@ -443,35 +541,47 @@ namespace relational } // All the columns we have in this table so far are for the - // object id. Add them to the foreign key and the index. + // object id. Add them to the foreign key. // for (sema_rel::table::names_iterator i (t.names_begin ()); i != t.names_end (); ++i) { - column& c (dynamic_cast (i->nameable ())); - - model_.new_edge (fk, c); - model_.new_edge (in, c); + if (column* c = dynamic_cast (&i->nameable ())) + model_.new_edge (fk, *c); } - // Derive the names. See the comment for the other foreign key - // code above. - // - // Note also that id_name can be a column prefix (if id is - // composite), in which case it can be empty and if not, then - // it will most likely already contain a trailing underscore. + // Index. See if we have a custom index. // - string id_name (column_name (m, "id", "object_id")); - - if (id_name.empty ()) - id_name = "object_id"; + index* sin (m.count ("id-index") ? &m.get ("id-index") : 0); + sema_rel::index* in (0); - if (id_name[id_name.size () - 1] != '_') - id_name += '_'; + if (sin != 0) + { + in = &model_.new_node ( + id + ".id", sin->type, sin->method, sin->options); + in->set ("cxx-location", sin->loc); + model_.new_edge ( + t, *in, (sin->name.empty () ? id_name + "i" : sin->name)); + } + else + { + in = &model_.new_node (id + ".id"); + in->set ("cxx-location", m.location ()); + model_.new_edge (t, *in, id_name + "i"); + } - model_.new_edge (t, fk, id_name + "fk"); - model_.new_edge (t, in, id_name + "i"); + // All the columns we have in this table so far are for the + // object id. Add them to the index. + // + for (sema_rel::table::names_iterator i (t.names_begin ()); + i != t.names_end (); + ++i) + { + if (column* c = dynamic_cast (&i->nameable ())) + model_.new_edge ( + *in, *c, (sin != 0 ? sin->members.back ().options : "")); + } } // index (simple value) @@ -479,18 +589,39 @@ namespace relational bool ordered (ck == ck_ordered && !unordered (m)); if (ordered) { + // Column. + // instance oc (model_, t); oc->traverse (m, container_it (ct), "index", "index"); string col_name (column_name (m, "index", "index")); - index& in (model_.new_node (id + ".index")); - in.set ("cxx-node", static_cast (&m)); + // Index. See if we have a custom index. + // + index* sin (m.count ("index-index") + ? &m.get ("index-index") + : 0); + sema_rel::index* in (0); - model_.new_edge ( - in, dynamic_cast (t.find (col_name)->nameable ())); + if (sin != 0) + { + in = &model_.new_node ( + id + ".index", sin->type, sin->method, sin->options); + in->set ("cxx-location", sin->loc); + model_.new_edge ( + t, *in, (sin->name.empty () ? col_name + "_i" : sin->name)); + } + else + { + in = &model_.new_node (id + ".index"); + in->set ("cxx-location", m.location ()); + model_.new_edge (t, *in, col_name + "_i"); + } - model_.new_edge (t, in, col_name + "_i"); + model_.new_edge ( + *in, + dynamic_cast (t.find (col_name)->nameable ()), + (sin != 0 ? sin->members.back ().options : "")); } // key @@ -551,18 +682,25 @@ namespace relational sema_rel::object_table& t( model_.new_node (id)); - - t.set ("cxx-node", static_cast (&c)); - + t.set ("cxx-location", c.location ()); model_.new_edge (model_, t, name); sema_rel::model::names_iterator begin (--model_.names_end ()); + // Add columns. + // { instance oc (model_, t); oc->traverse (c); } + // Add indexes. + // + { + instance oi (model_, t); + oi->traverse (c); + } + tables_.insert (name); // Create tables for members. diff --git a/odb/relational/mysql/schema.cxx b/odb/relational/mysql/schema.cxx index 4050d70..8068fc4 100644 --- a/odb/relational/mysql/schema.cxx +++ b/odb/relational/mysql/schema.cxx @@ -125,6 +125,36 @@ namespace relational } }; entry create_table_; + + struct create_index: relational::create_index, context + { + create_index (base const& x): base (x) {} + + virtual void + create (sema_rel::index& in) + { + os << "CREATE "; + + if (!in.type ().empty ()) + os << in.type () << ' '; + + os << "INDEX " << name (in); + + if (!in.method ().empty ()) + os << " USING " << in.method (); + + os << endl + << " ON " << table_name (in) << " ("; + + columns (in); + + os << ")" << endl; + + if (!in.options ().empty ()) + os << ' ' << in.options () << endl; + } + }; + entry create_index_; } } } diff --git a/odb/relational/pgsql/schema.cxx b/odb/relational/pgsql/schema.cxx index 2113894..5574bdb 100644 --- a/odb/relational/pgsql/schema.cxx +++ b/odb/relational/pgsql/schema.cxx @@ -4,6 +4,7 @@ #include +#include #include #include @@ -74,11 +75,10 @@ namespace relational os << "BIGSERIAL"; else { - semantics::node& n (*c.get ("cxx-node")); + location const& l (c.get ("cxx-location")); - cerr << n.file () << ":" << n.line () << ":" << n.column () - << ": error: automatically assigned object id must map " - << "to PostgreSQL INTEGER or BIGINT" << endl; + error (l) << "automatically assigned object id must map " + << "to PostgreSQL INTEGER or BIGINT" << endl; throw operation_failed (); } @@ -181,6 +181,49 @@ namespace relational static_cast (in.scope ()).name ().uname () + "_" + in.name ()); } + + virtual void + create (sema_rel::index& in) + { + os << "CREATE "; + + if (!in.type ().empty ()) + { + // Handle the CONCURRENTLY keyword. + // + string const& t (in.type ()); + + if (t == "concurrently" || t == "CONCURRENTLY") + { + os << "INDEX " << t; + } + else + { + size_t p (t.rfind (' ')); + string s (t, (p != string::npos ? p + 1 : 0), string::npos); + + if (s == "concurrently" || s == "CONCURRENTLY") + os << string (t, 0, p) << " INDEX " << s; + else + os << t << " INDEX"; + } + } + else + os << "INDEX"; + + os << " " << name (in) << endl + << " ON " << table_name (in); + + if (!in.method ().empty ()) + os << " USING " << in.method (); + + os << " ("; + columns (in); + os << ")" << endl; + + if (!in.options ().empty ()) + os << ' ' << in.options () << endl; + } }; entry create_index_; } diff --git a/odb/relational/processor.cxx b/odb/relational/processor.cxx index 5cb5f35..f496a0d 100644 --- a/odb/relational/processor.cxx +++ b/odb/relational/processor.cxx @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -119,6 +120,8 @@ namespace relational if (transient (m)) return; + process_index (m); + semantics::names* hint; semantics::type& t (utype (m, hint)); @@ -290,6 +293,44 @@ namespace relational throw operation_failed (); } + // Convert index/unique specifiers to the index entry in the object. + // + void + process_index (semantics::data_member& m) + { + bool ip (m.count ("index")); + bool up (m.count ("unique")); + + if (ip || up) + { + using semantics::class_; + class_& c (dynamic_cast (m.scope ())); + + indexes& ins (c.count ("index") + ? c.get ("index") + : c.set ("index", indexes ())); + + index in; + in.loc = m.get ( + ip ? "index-location" : "unique-location"); + + if (up) + in.type = "UNIQUE"; + + index::member im; + im.loc = in.loc; + im.name = m.name (); + im.path.push_back (&m); + in.members.push_back (im); + + // Insert it in the location order. + // + ins.insert ( + lower_bound (ins.begin (), ins.end (), in, index_comparator ()), + in); + } + } + void process_container_value (semantics::type& t, semantics::names* hint, @@ -1323,7 +1364,7 @@ namespace relational tree type; cpp_ttype ptt; // Not used. decl = lookup::resolve_scoped_name ( - lex_, tt, tl, tn, ptt, i->scope, name, false, &type); + lex_, tt, tl, tn, ptt, i->scope, name, false, false, &type); type = TYPE_MAIN_VARIANT (type); @@ -1801,16 +1842,18 @@ namespace relational if (k == class_object || k == class_view) assign_pointer (c); - // Do some additional pre-processing for objects. + // Do some additional pre-processing. // if (k == class_object) traverse_object_pre (c); names (c); - // Do some additional post-processing for views. + // Do some additional post-processing. // - if (k == class_view) + if (k == class_object) + traverse_object_post (c); + else if (k == class_view) traverse_view_post (c); } @@ -1957,6 +2000,191 @@ namespace relational } } + virtual void + traverse_object_post (type& c) + { + // Process indexes. Here we need to do two things: resolve member + // names to member paths and assign names to unnamed indexes. We + // are also going to handle the special container indexes. + // + indexes& ins (c.count ("index") + ? c.get ("index") + : c.set ("index", indexes ())); + + for (indexes::iterator i (ins.begin ()); i != ins.end ();) + { + index& in (*i); + + // This should never happen since a db index pragma without + // the member specifier will be treated as a member pragma. + // + assert (!in.members.empty ()); + + // First resolve member names. + // + string tl; + tree tn; + cpp_ttype tt; + + index::members_type::iterator j (in.members.begin ()); + for (; j != in.members.end (); ++j) + { + index::member& im (*j); + + if (!im.path.empty ()) + continue; // Already resolved. + + try + { + lex_.start (im.name); + tt = lex_.next (tl, &tn); + + // The name was already verified to be syntactically correct so + // we don't need to do any error checking. + // + string name; // Not used. + cpp_ttype ptt; // Not used. + tree decl ( + lookup::resolve_scoped_name ( + lex_, tt, tl, tn, ptt, c.tree_node (), name, false)); + + // Check that we have a data member. + // + if (TREE_CODE (decl) != FIELD_DECL) + { + error (im.loc) << "name '" << tl << "' in db pragma member " + << "does not refer to a data member" << endl; + throw operation_failed (); + } + + using semantics::data_member; + + data_member* m (dynamic_cast (unit.find (decl))); + im.path.push_back (m); + + if (container (*m)) + break; + + // Resolve nested members if any. + // + for (; tt == CPP_DOT; tt = lex_.next (tl, &tn)) + { + lex_.next (tl, &tn); // Get CPP_NAME. + + tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl))); + + decl = lookup_qualified_name ( + type, get_identifier (tl.c_str ()), false, false); + + if (decl == error_mark_node || TREE_CODE (decl) != FIELD_DECL) + { + error (im.loc) << "name '" << tl << "' in db pragma member " + << "does not refer to a data member" << endl; + throw operation_failed (); + } + + m = dynamic_cast (unit.find (decl)); + im.path.push_back (m); + + if (container (*m)) + { + tt = lex_.next (tl, &tn); // Get CPP_DOT. + break; // Only breaks out of the inner loop. + } + } + + if (container (*m)) + break; + } + catch (lookup::invalid_name const&) + { + error (im.loc) << "invalid name in db pragma member" << endl; + throw operation_failed (); + } + catch (lookup::unable_to_resolve const& e) + { + error (im.loc) << "unable to resolve name '" << e.name () + << "' in db pragma member" << endl; + throw operation_failed (); + } + } + + // Handle container indexes. + // + if (j != in.members.end ()) + { + // Do some sanity checks. + // + if (in.members.size () != 1) + { + error (in.loc) << "multiple data members specified for a " + << "container index" << endl; + throw operation_failed (); + } + + if (tt != CPP_DOT || lex_.next (tl, &tn) != CPP_NAME || + (tl != "id" && tl != "index")) + { + error (j->loc) << ".id or .index special member expected in a " + << "container index" << endl; + throw operation_failed (); + } + + string n (tl); + + if (lex_.next (tl, &tn) != CPP_EOF) + { + error (j->loc) << "unexpected text after ." << n << " in " + << "db pragma member" << endl; + throw operation_failed (); + } + + // Move this index to the container member. + // + j->path.back ()->set (n + "-index", *i); + i = ins.erase (i); + continue; + } + + // Now assign the name if the index is unnamed. + // + if (in.name.empty ()) + { + // Make sure there is only one member. + // + if (in.members.size () > 1) + { + error (in.loc) << "unnamed index with more than one data " + << "member" << endl; + throw operation_failed (); + } + + // Generally, we want the index name to be based on the column + // name. This is straightforward for single-column members. In + // case of a composite member, we will need to use the column + // prefix which is based on the data member name, unless + // overridden by the user. In the latter case the prefix can + // be empty, in which case we will just fall back on the + // member's public name. + // + in.name = column_name (in.members.front ().path); + + if (in.name.empty ()) + in.name = public_name_db (*in.members.front ().path.back ()); + + // In case of a composite member, column_name() return a column + // prefix which already includes the trailing underscore. + // + if (in.name[in.name.size () - 1] != '_') + in.name += '_'; + + in.name += 'i'; + } + + ++i; + } + } + // // View. // @@ -2251,14 +2479,14 @@ namespace relational name += "::"; name += r.name; - lexer.start (name); + lex_.start (name); string t; - for (cpp_ttype tt (lexer.next (t)); + for (cpp_ttype tt (lex_.next (t)); tt != CPP_EOF; - tt = lexer.next (t)) + tt = lex_.next (t)) { - cxx_token ct (lexer.location (), tt); + cxx_token ct (lex_.location (), tt); ct.literal = t; i->cond.push_back (ct); } @@ -2618,16 +2846,16 @@ namespace relational // try { - lexer.start (ptr); + lex_.start (ptr); ptr.clear (); string t; bool punc (false); bool scoped (false); - for (cpp_ttype tt (lexer.next (t)); + for (cpp_ttype tt (lex_.next (t)); tt != CPP_EOF; - tt = lexer.next (t)) + tt = lex_.next (t)) { if (punc && tt > CPP_LAST_PUNCTUATOR) ptr += ' '; @@ -2750,12 +2978,12 @@ namespace relational tree tn; cpp_ttype tt, ptt; - nested_lexer.start (qn); - tt = nested_lexer.next (tl, &tn); + nlex_.start (qn); + tt = nlex_.next (tl, &tn); string name; return lookup::resolve_scoped_name ( - nested_lexer, tt, tl, tn, ptt, scope, name, is_type); + nlex_, tt, tl, tn, ptt, scope, name, is_type); } catch (cxx_lexer::invalid_input const&) { @@ -2768,8 +2996,8 @@ namespace relational } private: - cxx_string_lexer lexer; - cxx_string_lexer nested_lexer; + cxx_string_lexer lex_; + cxx_string_lexer nlex_; // Nested lexer. data_member member_; traversal::names member_names_; diff --git a/odb/relational/schema.hxx b/odb/relational/schema.hxx index 6551119..e6d4824 100644 --- a/odb/relational/schema.hxx +++ b/odb/relational/schema.hxx @@ -494,13 +494,10 @@ namespace relational } virtual void - create (sema_rel::index& in) + columns (sema_rel::index& in) { using sema_rel::index; - os << "CREATE INDEX " << name (in) << endl - << " ON " << table_name (in) << " ("; - for (index::contains_iterator i (in.contains_begin ()); i != in.contains_end (); ++i) @@ -515,9 +512,31 @@ namespace relational } os << quote_id (i->column ().name ()); + + if (!i->options ().empty ()) + os << ' ' << i->options (); } + } + + virtual void + create (sema_rel::index& in) + { + // Default implementation that ignores the method. + // + os << "CREATE "; + + if (!in.type ().empty ()) + os << in.type () << ' '; + + os << "INDEX " << name (in) << endl + << " ON " << table_name (in) << " ("; + + columns (in); os << ")" << endl; + + if (!in.options ().empty ()) + os << ' ' << in.options () << endl; } protected: diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx index ee7ac21..a338b09 100644 --- a/odb/relational/source.cxx +++ b/odb/relational/source.cxx @@ -3436,7 +3436,7 @@ namespace relational // tree type; decl = lookup::resolve_scoped_name ( - l, tt, tl, tn, ptt, scope, name, false, &type); + l, tt, tl, tn, ptt, scope, name, false, false, &type); type = TYPE_MAIN_VARIANT (type); diff --git a/odb/relational/validator.cxx b/odb/relational/validator.cxx index ec78e8f..1269406 100644 --- a/odb/relational/validator.cxx +++ b/odb/relational/validator.cxx @@ -2,6 +2,7 @@ // copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file +#include #include #include @@ -14,6 +15,95 @@ using namespace std; namespace relational { + namespace + { + // + // Pass 2. + // + + struct class2: traversal::class_, context + { + class2 (bool& valid) + : valid_ (valid) + { + } + + virtual void + traverse (type& c) + { + if (object (c)) + traverse_object (c); + else if (view (c)) + traverse_view (c); + else if (composite (c)) + traverse_composite (c); + + // Make sure indexes are not defined for anything other than objects. + // + if (c.count ("index") && !object (c)) + { + indexes& ins (c.get ("index")); + + for (indexes::iterator i (ins.begin ()); i != ins.end (); ++i) + { + error (i->loc) << "index definition on a non-persistent class" + << endl; + valid_ = false; + } + } + } + + virtual void + traverse_object (type& c) + { + // Validate indexes. + // + { + indexes& ins (c.get ("index")); + + // Make sure index members are not transient or containers. + // + for (indexes::iterator i (ins.begin ()); i != ins.end (); ++i) + { + index& in (*i); + + for (index::members_type::iterator i (in.members.begin ()); + i != in.members.end (); ++i) + { + index::member& im (*i); + semantics::data_member& m (*im.path.back ()); + + if (transient (m)) + { + error (im.loc) << "index member is transient" << endl; + valid_ = false; + } + + if (container (m)) + { + error (im.loc) << "index member is a container" << endl; + valid_ = false; + } + } + } + } + } + + virtual void + traverse_view (type&) + { + } + + virtual void + traverse_composite (type&) + { + } + + public: + bool& valid_; + }; + } + void validator:: validate (options const&, features&, @@ -95,5 +185,33 @@ namespace relational if (!valid) throw failed (); + + if (pass == 1) + { + } + 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; + + traversal::defines ns_defines; + typedefs ns_typedefs (true); + + ns >> ns_defines >> ns; + ns_defines >> c; + ns >> ns_typedefs >> c; + + unit.dispatch (u); + } + + if (!valid) + throw failed (); } } diff --git a/odb/semantics/elements.cxx b/odb/semantics/elements.cxx index 4b8fcea..ddae187 100644 --- a/odb/semantics/elements.cxx +++ b/odb/semantics/elements.cxx @@ -27,13 +27,13 @@ namespace semantics // node:: node (path const& file, size_t line, size_t column, tree tn) - : tree_node_ (tn), file_ (file), line_ (line), column_ (column) + : tree_node_ (tn), loc_ (file, line, column) { } node:: node () - : file_ ("") + : loc_ (0) { // GCC plugin machinery #define's abort as a macro. // diff --git a/odb/semantics/elements.hxx b/odb/semantics/elements.hxx index 59e058d..ac62ff9 100644 --- a/odb/semantics/elements.hxx +++ b/odb/semantics/elements.hxx @@ -20,6 +20,7 @@ #include #include +#include namespace semantics { @@ -134,22 +135,30 @@ namespace semantics } public: + typedef ::location location_type; + path const& file () const { - return file_; + return loc_.file; } size_t line () const { - return line_; + return loc_.line; } size_t column () const { - return column_; + return loc_.column; + } + + location_type const& + location () const + { + return loc_; } public: @@ -204,9 +213,7 @@ namespace semantics tree tree_node_; unit_type* unit_; - path file_; - size_t line_; - size_t column_; + location_type loc_; }; // diff --git a/odb/semantics/relational/index.hxx b/odb/semantics/relational/index.hxx index af90b12..bf08a23 100644 --- a/odb/semantics/relational/index.hxx +++ b/odb/semantics/relational/index.hxx @@ -17,13 +17,42 @@ namespace semantics class index: public key { public: - index (string const& id): key (id) {} + index (string const& id, + string const& t = string (), + string const& m = string (), + string const& o = string ()) + : key (id), type_ (t), method_ (m), options_ (o) + { + } + + string const& + type () const + { + return type_; + } + + string const& + method () const + { + return method_; + } + + string const& + options () const + { + return options_; + } virtual string kind () const { return "index"; } + + private: + string type_; // E.g., "UNIQUE", etc. + string method_; // E.g., "BTREE", etc. + string options_; // Database-specific index options. }; } } diff --git a/odb/semantics/relational/key.hxx b/odb/semantics/relational/key.hxx index 40e7499..3b86f1c 100644 --- a/odb/semantics/relational/key.hxx +++ b/odb/semantics/relational/key.hxx @@ -32,7 +32,15 @@ namespace semantics return *column_; } + string const& + options () const + { + return options_; + } + public: + contains (string const& o = string ()) : options_ (o) {} + void set_left_node (key_type& n) { @@ -48,6 +56,7 @@ namespace semantics protected: key_type* key_; column_type* column_; + string options_; }; class key: public unameable diff --git a/odb/validator.cxx b/odb/validator.cxx index 597da01..0f67100 100644 --- a/odb/validator.cxx +++ b/odb/validator.cxx @@ -1236,57 +1236,54 @@ validate (options const& ops, unsigned short pass) { bool valid (true); + auto_ptr ctx (create_context (cerr, u, ops, f, 0)); + if (pass == 1) { - 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); + 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) -- cgit v1.1