From d1ad30f7a517e69bc87d1347224f1c9ab38493b3 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 25 Oct 2012 10:35:36 +0200 Subject: Static multi-database support Add new options (--multi-database, --default-database). Generate common code to -odb.?xx files and database-specific to -odb-.?xx. --- odb/context.cxx | 9 +- odb/generator.cxx | 90 +++- odb/header.cxx | 321 +++++++++++- odb/include.cxx | 2 +- odb/inline.cxx | 220 ++++++++ odb/makefile | 3 +- odb/odb.cxx | 658 ++++++++++++----------- odb/option-functions.cxx | 61 ++- odb/option-parsers.hxx | 89 ++++ odb/option-types.cxx | 45 ++ odb/option-types.hxx | 50 ++ odb/options.cli | 30 +- odb/plugin.cxx | 2 +- odb/processor.cxx | 1177 +++++++++++++++++++++++++++++++++++++++++- odb/profile.cxx | 8 +- odb/relational/common.hxx | 2 +- odb/relational/header.cxx | 274 +--------- odb/relational/header.hxx | 3 +- odb/relational/inline.hxx | 180 +------ odb/relational/processor.cxx | 1102 +-------------------------------------- odb/relational/source.cxx | 4 +- odb/relational/validator.cxx | 296 ++++++++++- odb/validator.cxx | 429 ++++----------- 23 files changed, 2852 insertions(+), 2203 deletions(-) create mode 100644 odb/option-parsers.hxx (limited to 'odb') diff --git a/odb/context.cxx b/odb/context.cxx index 099b10b..2350ad1 100644 --- a/odb/context.cxx +++ b/odb/context.cxx @@ -386,8 +386,13 @@ create_context (ostream& os, { auto_ptr r; - switch (ops.database ()) + switch (ops.database ()[0]) { + case database::common: + { + r.reset (new context (os, unit, ops, f)); + break; + } case database::mssql: { r.reset (new relational::mssql::context (os, unit, ops, f, m)); @@ -436,7 +441,7 @@ context (ostream& os_, unit (u), options (ops), features (f), - db (options.database ()), + db (options.database ()[0]), keyword_set (data_->keyword_set_), include_regex (data_->include_regex_), accessor_regex (data_->accessor_regex_), diff --git a/odb/generator.cxx b/odb/generator.cxx index 5500860..d6456d9 100644 --- a/odb/generator.cxx +++ b/odb/generator.cxx @@ -78,6 +78,8 @@ generate (options const& ops, { try { + database db (ops.database ()[0]); + // First create the database model. // cutl::shared_ptr model; @@ -86,8 +88,12 @@ generate (options const& ops, { auto_ptr ctx (create_context (cerr, unit, ops, fts, 0)); - switch (ops.database ()) + switch (db) { + case database::common: + { + break; // No schema for common. + } case database::mssql: case database::mysql: case database::oracle: @@ -109,11 +115,11 @@ generate (options const& ops, fs::auto_removes auto_rm; - string hxx_name (base + ops.odb_file_suffix () + ops.hxx_suffix ()); - string ixx_name (base + ops.odb_file_suffix () + ops.ixx_suffix ()); - string cxx_name (base + ops.odb_file_suffix () + ops.cxx_suffix ()); - string sch_name (base + ops.schema_file_suffix () + ops.cxx_suffix ()); - string sql_name (base + ops.sql_suffix ()); + string hxx_name (base + ops.odb_file_suffix ()[db] + ops.hxx_suffix ()); + string ixx_name (base + ops.odb_file_suffix ()[db] + ops.ixx_suffix ()); + string cxx_name (base + ops.odb_file_suffix ()[db] + ops.cxx_suffix ()); + string sch_name (base + ops.schema_file_suffix ()[db] + ops.cxx_suffix ()); + string sql_name (base + ops.sql_file_suffix ()[db] + ops.sql_suffix ()); path hxx_path (hxx_name); path ixx_path (ixx_name); @@ -173,7 +179,7 @@ generate (options const& ops, // ofstream cxx; - if (gen_cxx) + if (gen_cxx && db != database::common) { cxx.open (cxx_path.string ().c_str (), ios_base::out); @@ -190,7 +196,8 @@ generate (options const& ops, // // bool gen_sql_schema (ops.generate_schema () && - ops.schema_format ().count (schema_format::sql)); + ops.schema_format ().count (schema_format::sql) && + db != database::common); ofstream sql; if (gen_sql_schema) @@ -209,9 +216,11 @@ generate (options const& ops, // // - bool gen_sep_schema (gen_cxx && - ops.generate_schema () && - ops.schema_format ().count (schema_format::separate)); + bool gen_sep_schema ( + gen_cxx && + ops.generate_schema () && + ops.schema_format ().count (schema_format::separate) && + db != database::common); ofstream sch; @@ -235,7 +244,9 @@ generate (options const& ops, { hxx << cxx_file_header; ixx << cxx_file_header; - cxx << cxx_file_header; + + if (db != database::common) + cxx << cxx_file_header; } if (gen_sep_schema) @@ -299,10 +310,8 @@ generate (options const& ops, for (paths::const_iterator i (inputs.begin ()); i != inputs.end (); ++i) hxx << "#include " << ctx->process_include_path (i->leaf ().string ()) << endl; - hxx << endl; - { // We don't want to indent prologues/epilogues. // @@ -314,16 +323,31 @@ generate (options const& ops, if (!ops.at_once ()) include::generate (true); - header::generate (); - - switch (ops.database ()) + switch (db) { + case database::common: + { + header::generate (); + break; + } case database::mssql: case database::mysql: case database::oracle: case database::pgsql: case database::sqlite: { + if (ops.multi_database () == multi_database::disabled) + header::generate (); + else + { + string n (base + + ops.odb_file_suffix ()[database::common] + + ops.hxx_suffix ()); + + hxx << "#include " << ctx->process_include_path (n) << endl + << endl; + } + relational::header::generate (); break; } @@ -382,16 +406,22 @@ generate (options const& ops, // ind_filter ind (ctx->os); - inline_::generate (); - - switch (ops.database ()) + switch (db) { + case database::common: + { + inline_::generate (); + break; + } case database::mssql: case database::mysql: case database::oracle: case database::pgsql: case database::sqlite: { + if (ops.multi_database () == multi_database::disabled) + inline_::generate (); + relational::inline_::generate (); break; } @@ -417,7 +447,7 @@ generate (options const& ops, // CXX // - if (gen_cxx) + if (gen_cxx && db != database::common) { auto_ptr ctx ( create_context (cxx, unit, ops, fts, model.get ())); @@ -453,8 +483,12 @@ generate (options const& ops, if (!ops.at_once ()) include::generate (false); - switch (ops.database ()) + switch (db) { + case database::common: + { + assert (false); + } case database::mssql: case database::mysql: case database::oracle: @@ -520,8 +554,12 @@ generate (options const& ops, // ind_filter ind (ctx->os); - switch (ops.database ()) + switch (db) { + case database::common: + { + assert (false); + } case database::mssql: case database::mysql: case database::oracle: @@ -562,8 +600,12 @@ generate (options const& ops, auto_ptr ctx ( create_context (sql, unit, ops, fts, model.get ())); - switch (ops.database ()) + switch (db) { + case database::common: + { + assert (false); + } case database::mssql: case database::mysql: case database::oracle: diff --git a/odb/header.cxx b/odb/header.cxx index 8e39b75..67e4cc0 100644 --- a/odb/header.cxx +++ b/odb/header.cxx @@ -10,6 +10,297 @@ using namespace std; namespace header { + struct class_: traversal::class_, virtual context + { + virtual void + traverse (type& c) + { + if (!options.at_once () && class_file (c) != unit.file ()) + return; + + if (object (c)) + traverse_object (c); + else if (view (c)) + traverse_view (c); + } + + void + traverse_object (type&); + + void + traverse_view (type&); + + void + traverse_composite (type&); + }; +} + +void header::class_:: +traverse_object (type& c) +{ + using semantics::data_member; + + data_member* id (id_member (c)); + bool auto_id (id && auto_ (*id)); + bool base_id (id && &id->scope () != &c); // Comes from base. + + data_member* optimistic (context::optimistic (c)); + + type* poly_root (polymorphic (c)); + bool poly (poly_root != 0); + bool poly_derived (poly && poly_root != &c); + type* poly_base (poly_derived ? &polymorphic_base (c) : 0); + data_member* discriminator (poly ? context::discriminator (*poly_root) : 0); + + bool abst (abstract (c)); + bool reuse_abst (abst && !poly); + + string const& type (class_fq_name (c)); + + os << "// " << class_name (c) << endl + << "//" << endl; + + // class_traits + // + os << "template <>" << endl + << "struct class_traits< " << type << " >" + << "{" + << "static const class_kind kind = class_object;" + << "};"; + + // object_traits + // + os << "template <>" << endl + << "class access::object_traits< " << type << " >" + << "{" + << "public:" << endl; + + // object_type & pointer_type + // + os << "typedef " << type << " object_type;" + << "typedef " << c.get ("object-pointer") << " pointer_type;" + << "typedef odb::pointer_traits pointer_traits;" + << endl; + + // polymorphic, root_type, base_type, etc. + // + os << "static const bool polymorphic = " << (poly ? "true" : "false") << ";" + << endl; + + if (poly) + { + os << "typedef " << class_fq_name (*poly_root) << " root_type;"; + + if (poly_derived) + { + os << "typedef " << class_fq_name (*poly_base) << " base_type;" + << "typedef object_traits::discriminator_type " << + "discriminator_type;" + << "typedef polymorphic_concrete_info info_type;"; + + if (abst) + os << "typedef polymorphic_abstract_info " << + "abstract_info_type;"; + + // Calculate our hierarchy depth (number of classes). + // + size_t depth (polymorphic_depth (c)); + + os << endl + << "static const std::size_t depth = " << depth << "UL;"; + } + else + { + semantics::names* hint; + semantics::type& t (utype (*discriminator, hint)); + + os << "typedef " << t.fq_name (hint) << " discriminator_type;" + << "typedef polymorphic_map map_type;" + << "typedef polymorphic_concrete_info info_type;"; + + if (abst) + os << "typedef polymorphic_abstract_info " << + "abstract_info_type;"; + + os << endl + << "static const std::size_t depth = 1UL;"; + } + + os << endl; + } + + // id_type, version_type, etc. + // + if (id != 0) + { + if (base_id) + { + semantics::class_& b ( + dynamic_cast (id->scope ())); + string const& type (class_fq_name (b)); + + os << "typedef object_traits< " << type << " >::id_type id_type;"; + + if (optimistic != 0) + os << "typedef object_traits< " << type << " >::version_type " << + "version_type;"; + + os << endl; + + if (poly_derived) + os << "static const bool auto_id = false;"; + else + os << "static const bool auto_id = object_traits< " << type << + " >::auto_id;"; + } + else + { + { + semantics::names* hint; + semantics::type& t (utype (*id, hint)); + os << "typedef " << t.fq_name (hint) << " id_type;"; + } + + if (optimistic != 0) + { + semantics::names* hint; + semantics::type& t (utype (*optimistic, hint)); + os << "typedef " << t.fq_name (hint) << " version_type;"; + } + + os << endl + << "static const bool auto_id = " << (auto_id ? "true;" : "false;"); + } + + os << endl; + } + else if (!reuse_abst) + { + // Object without id. + // + os << "typedef void id_type;" + << endl + << "static const bool auto_id = false;" + << endl; + } + + // abstract + // + os << "static const bool abstract = " << (abst ? "true" : "false") << ";" + << endl; + + // id () + // + if (id != 0 || !reuse_abst) + { + // We want to generate a dummy void id() accessor even if this + // object has no id to help us in the runtime. This way we can + // write generic code that will work for both void and non-void + // ids. + // + os << "static id_type" << endl + << "id (const object_type&);" + << endl; + } + + if (!reuse_abst) + { + // Cache traits typedefs. + // + if (id == 0) + { + os << "typedef" << endl + << "no_id_pointer_cache_traits" << endl + << "pointer_cache_traits;" + << endl + << "typedef" << endl + << "no_id_reference_cache_traits" << endl + << "reference_cache_traits;" + << endl; + } + else + { + char const* p (session (c) ? "odb::" : "no_op_"); + + if (poly_derived) + { + os << "typedef" << endl + << p << "pointer_cache_traits<" << + "object_traits::pointer_type>" << endl + << "pointer_cache_traits;" + << endl + << "typedef" << endl + << p << "reference_cache_traits" << endl + << "reference_cache_traits;" + << endl; + } + else + { + os << "typedef" << endl + << p << "pointer_cache_traits" << endl + << "pointer_cache_traits;" + << endl + << "typedef" << endl + << p << "reference_cache_traits" << endl + << "reference_cache_traits;" + << endl; + } + } + + // callback () + // + os << "static void" << endl + << "callback (database&, object_type&, callback_event);" + << endl; + + os << "static void" << endl + << "callback (database&, const object_type&, callback_event);" + << endl; + } + + os << "};"; +} + +void header::class_:: +traverse_view (type& c) +{ + string const& type (class_fq_name (c)); + + os << "// " << class_name (c) << endl + << "//" << endl; + + // class_traits + // + os << "template <>" << endl + << "struct class_traits< " << type << " >" + << "{" + << "static const class_kind kind = class_view;" + << "};"; + + // view_traits + // + os << "template <>" << endl + << "class access::view_traits< " << type << " >" + << "{" + << "public:" << endl; + + // view_type & pointer_type + // + os << "typedef " << type << " view_type;" + << "typedef " << c.get ("object-pointer") << " pointer_type;" + << endl; + + // callback () + // + os << "static void" << endl + << "callback (database&, view_type&, callback_event);" + << endl; + + os << "};"; +} + +namespace header +{ void generate () { @@ -78,6 +369,34 @@ namespace header os << "#include " << endl; } - os << endl; + os << endl + << "#include " << endl + << endl; + + // Generate common code. + // + traversal::unit unit; + traversal::defines unit_defines; + typedefs unit_typedefs (false); + traversal::namespace_ ns; + class_ c; + + unit >> unit_defines >> ns; + unit_defines >> c; + unit >> unit_typedefs >> c; + + traversal::defines ns_defines; + typedefs ns_typedefs (false); + + ns >> ns_defines >> ns; + ns_defines >> c; + ns >> ns_typedefs >> c; + + os << "namespace odb" + << "{"; + + unit.dispatch (ctx.unit); + + os << "}"; } } diff --git a/odb/include.cxx b/odb/include.cxx index 3d17b9a..c842010 100644 --- a/odb/include.cxx +++ b/odb/include.cxx @@ -688,7 +688,7 @@ namespace include } path f (inc->path_.base ()); - f += ctx.options.odb_file_suffix (); + f += ctx.options.odb_file_suffix ()[ctx.options.database ()[0]]; f += ctx.options.hxx_suffix (); char o (inc->type_ == include_directive::quote ? '"' : '<'); diff --git a/odb/inline.cxx b/odb/inline.cxx index e39f85d..39bde4d 100644 --- a/odb/inline.cxx +++ b/odb/inline.cxx @@ -5,13 +5,233 @@ #include #include #include +#include using namespace std; namespace inline_ { + // + // + struct callback_calls: traversal::class_, virtual context + { + callback_calls () + { + *this >> inherits_ >> *this; + } + + void + traverse (type& c, bool constant) + { + const_ = constant; + traverse (c); + } + + virtual void + traverse (type& c) + { + bool obj (object (c)); + + // Ignore transient bases. + // + if (!(obj || view (c))) + return; + + if (c.count ("callback")) + { + string name (c.get ("callback")); + + // In case of the const instance, we only generate the call if + // there is a const callback. Note also that we cannot use + // object_type/view_type alias because it can be a base type. + // + string const& type (class_fq_name (c)); + + if (const_) + { + if (c.count ("callback-const")) + os << "static_cast (x)." << name << + " (e, db);"; + } + else + os << "static_cast< " << type << "&> (x)." << name << " (e, db);"; + } + else if (obj) + inherits (c); + } + + protected: + bool const_; + traversal::inherits inherits_; + }; + + struct class_: traversal::class_, virtual context + { + virtual void + traverse (type& c) + { + if (!options.at_once () && class_file (c) != unit.file ()) + return; + + if (object (c)) + traverse_object (c); + else if (view (c)) + traverse_view (c); + } + + void + traverse_object (type&); + + void + traverse_view (type&); + + private: + callback_calls callback_calls_; + }; +} + +void inline_::class_:: +traverse_object (type& c) +{ + using semantics::data_member; + + data_member* id (id_member (c)); + bool base_id (id && &id->scope () != &c); // Comes from base. + + // Base class that contains the object id. + // + type* base (id != 0 && base_id ? dynamic_cast (&id->scope ()) : 0); + + bool poly (polymorphic (c)); + bool abst (abstract (c)); + bool reuse_abst (abst && !poly); + + string const& type (class_fq_name (c)); + string traits ("access::object_traits< " + type + " >"); + + os << "// " << class_name (c) << endl + << "//" << endl + << endl; + + // id (object_type) + // + if (id != 0 || !reuse_abst) + { + os << "inline" << endl + << traits << "::id_type" << endl + << traits << "::" << endl + << "id (const object_type&" << (id != 0 ? " o" : "") << ")" + << "{"; + + if (id != 0) + { + if (base_id) + os << "return object_traits< " << class_fq_name (*base) << + " >::id (o);"; + else + { + // Get the id using the accessor expression. If this is not + // a synthesized expression, then output its location for + // easier error tracking. + // + member_access& ma (id->get ("get")); + + if (!ma.synthesized) + os << "// From " << location_string (ma.loc, true) << endl; + + os << "return " << ma.translate ("o") << ";"; + } + } + + os << "}"; + } + + // The rest does not apply to reuse-abstract objects. + // + if (reuse_abst) + return; + + // callback () + // + os << "inline" << endl + << "void " << traits << "::" << endl + << "callback (database& db, object_type& x, callback_event e)" + << endl + << "{" + << "ODB_POTENTIALLY_UNUSED (db);" + << "ODB_POTENTIALLY_UNUSED (x);" + << "ODB_POTENTIALLY_UNUSED (e);" + << endl; + callback_calls_.traverse (c, false); + os << "}"; + + os << "inline" << endl + << "void " << traits << "::" << endl + << "callback (database& db, const object_type& x, callback_event e)" + << "{" + << "ODB_POTENTIALLY_UNUSED (db);" + << "ODB_POTENTIALLY_UNUSED (x);" + << "ODB_POTENTIALLY_UNUSED (e);" + << endl; + callback_calls_.traverse (c, true); + os << "}"; +} + +void inline_::class_:: +traverse_view (type& c) +{ + string const& type (class_fq_name (c)); + string traits ("access::view_traits< " + type + " >"); + + os << "// " << class_name (c) << endl + << "//" << endl + << endl; + + // callback () + // + os << "inline" << endl + << "void " << traits << "::" << endl + << "callback (database& db, view_type& x, callback_event e)" + << endl + << "{" + << "ODB_POTENTIALLY_UNUSED (db);" + << "ODB_POTENTIALLY_UNUSED (x);" + << "ODB_POTENTIALLY_UNUSED (e);" + << endl; + callback_calls_.traverse (c, false); + os << "}"; +} + +namespace inline_ +{ void generate () { + context ctx; + ostream& os (ctx.os); + + traversal::unit unit; + traversal::defines unit_defines; + typedefs unit_typedefs (false); + traversal::namespace_ ns; + class_ c; + + unit >> unit_defines >> ns; + unit_defines >> c; + unit >> unit_typedefs >> c; + + traversal::defines ns_defines; + typedefs ns_typedefs (false); + + ns >> ns_defines >> ns; + ns_defines >> c; + ns >> ns_typedefs >> c; + + os << "namespace odb" + << "{"; + + unit.dispatch (ctx.unit); + + os << "}"; } } diff --git a/odb/makefile b/odb/makefile index c57ed9f..bb0deba 100644 --- a/odb/makefile +++ b/odb/makefile @@ -200,7 +200,8 @@ $(gen): cli_options += \ --generate-file-scanner \ --include-with-brackets \ --include-prefix odb \ ---guard-prefix ODB +--guard-prefix ODB \ +--cxx-prologue '\#include ' $(call include-dep,$(cxx_pod) $(cxx_dod) $(cxx_cod),\ $(cxx_pobj) $(cxx_dobj) $(cxx_cobj),$(gen)) diff --git a/odb/odb.cxx b/odb/odb.cxx index e18feeb..b80f577 100644 --- a/odb/odb.cxx +++ b/odb/odb.cxx @@ -123,6 +123,7 @@ profile_paths (strings const& args, char const* name); static char const* const db_macro[] = { + "-DODB_DATABASE_COMMON", "-DODB_DATABASE_MSSQL", "-DODB_DATABASE_MYSQL", "-DODB_DATABASE_ORACLE", @@ -491,7 +492,9 @@ main (int argc, char* argv[]) oi[1].option = "-p"; oi[2].option = "--profile"; - database db; + vector dbs; + bool show_sloc; + size_t sloc_limit; { oi[1].search_func = &profile_search_ignore; oi[2].search_func = &profile_search_ignore; @@ -527,14 +530,24 @@ main (int argc, char* argv[]) // Check that required options were specifed. // - if (!ops.database_specified ()) + dbs = ops.database (); + + if (dbs.empty ()) { e << argv[0] << ": error: no database specified with the --database " << "option" << endl; return 1; } - db = ops.database (); + if (dbs.size () > 1 && !ops.multi_database_specified ()) + { + e << argv[0] << ": error: --multi-database option required when " << + "multiple databases are specified"<< endl; + return 1; + } + + show_sloc = ops.show_sloc (); + sloc_limit = ops.sloc_limit_specified () ? ops.sloc_limit () : 0; // Translate some ODB options to GCC options. // @@ -566,26 +579,15 @@ main (int argc, char* argv[]) e << " " << *i << endl; } - // Second parse. + // Pass profile search paths (svc-path option). // - profile_data pd (prof_paths, db, argv[0]); - oi[1].search_func = &profile_search; - oi[2].search_func = &profile_search; - oi[1].arg = &pd; - oi[2].arg = &pd; - - cli::argv_file_scanner scan (ac, &av[0], oi, 3); - options ops (scan); - - size_t end (scan.end () - 1); // We have one less in plugin_args. - - if (end == plugin_args.size ()) + for (paths::const_iterator i (prof_paths.begin ()); + i != prof_paths.end (); ++i) { - e << argv[0] << ": error: input file expected" << endl; - return 1; + args.push_back (encode_plugin_option ("svc-path", i->string ())); } - // Add ODB macros. + // Add common ODB macros. // args.push_back ("-DODB_COMPILER"); @@ -595,396 +597,428 @@ main (int argc, char* argv[]) args.push_back ("-DODB_COMPILER_VERSION=" + ostr.str ()); } - args.push_back (db_macro[ops.database ()]); - - // Encode plugin options. + // Compile for each database. // - cli::options const& desc (options::description ()); - for (size_t i (0); i < end; ++i) + size_t sloc_total (0); + + for (vector::iterator i (dbs.begin ()); i != dbs.end (); ++i) { - string k, v; - string a (plugin_args[i]); + database db (*i); + strings db_args (args); - // Ignore certain options. + // Add database-specific ODB macro. // - if (a == "--") - { - // Ignore the option seperator since GCC doesn't understand it. - // - continue; - } - else if (a == "--std") - { - // Translated to GCC -std=. - // - ++i; - continue; - } + db_args.push_back (db_macro[db]); + + // Second parse. + // + profile_data pd (prof_paths, db, argv[0]); + oi[1].search_func = &profile_search; + oi[2].search_func = &profile_search; + oi[1].arg = &pd; + oi[2].arg = &pd; - cli::options::const_iterator it (desc.find (a)); + cli::argv_file_scanner scan (ac, &av[0], oi, 3); + options ops (scan); - if (it == desc.end ()) + size_t end (scan.end () - 1); // We have one less in plugin_args. + + if (end == plugin_args.size ()) { - e << argv[0] << ": ice: unexpected option '" << a << "'" << endl; + e << argv[0] << ": error: input file expected" << endl; return 1; } - if (a.size () > 2 && a[0] == '-' && a[1] == '-') - k = string (a, 2); // long format - else - k = string (a, 1); // short format - - // If there are more arguments then we may have a value. + // Encode plugin options. // - if (!it->flag ()) + cli::options const& desc (options::description ()); + for (size_t i (0); i < end; ++i) { - if (i + 1 == end) + string k, v; + string a (plugin_args[i]); + + // Ignore certain options. + // + if (a == "--") { - e << argv[0] << ": ice: expected argument for '" << a << "'" << endl; - return 1; + // Ignore the option seperator since GCC doesn't understand it. + // + continue; + } + else if (a == "--std") + { + // Translated to GCC -std=. + // + ++i; + continue; + } + else if (a == "-d" || a == "--databse") + { + // Ignore all other databases. + // + if (plugin_args[i + 1] != db.string ()) + { + ++i; + continue; + } } - v = plugin_args[++i]; - } - - args.push_back (encode_plugin_option (k, v)); - } - - // Pass profile search paths (svc-path option). - // - for (paths::const_iterator i (prof_paths.begin ()); - i != prof_paths.end (); ++i) - { - args.push_back (encode_plugin_option ("svc-path", i->string ())); - } - - // Reserve space for and remember the position of the svc-file - // option. - // - size_t svc_file_pos (args.size ()); - args.push_back (""); - - // If compiling multiple input files at once, pass them also with - // the --svc-file option. - // - bool at_once (ops.at_once () && plugin_args.size () - end > 1); - if (at_once) - { - if (ops.output_name ().empty ()) - { - e << "error: --output-name required when compiling multiple " << - "input files at once (--at-once)" << endl; - return 1; - } - - for (size_t i (end); i < plugin_args.size (); ++i) - args.push_back (encode_plugin_option ("svc-file", plugin_args[i])); - } + cli::options::const_iterator it (desc.find (a)); - // Create an execvp-compatible argument array. - // - typedef vector cstrings; - cstrings exec_args; + if (it == desc.end ()) + { + e << argv[0] << ": ice: unexpected option '" << a << "'" << endl; + return 1; + } - for (strings::const_iterator i (args.begin ()), end (args.end ()); - i != end; ++i) - { - exec_args.push_back (i->c_str ()); - } + if (a.size () > 2 && a[0] == '-' && a[1] == '-') + k = string (a, 2); // long format + else + k = string (a, 1); // short format - exec_args.push_back ("-"); // Compile stdin. - exec_args.push_back (0); + // If there are more arguments then we may have a value. + // + if (!it->flag ()) + { + if (i + 1 == end) + { + e << argv[0] << ": ice: expected argument for '" << a << "'" + << endl; + return 1; + } - // Iterate over the input files and compile each of them. - // - size_t sloc_total (0); + v = plugin_args[++i]; + } - for (; end < plugin_args.size (); ++end) - { - string name (at_once ? ops.output_name () : plugin_args[end]); + db_args.push_back (encode_plugin_option (k, v)); + } - // Set the --svc-file option. + // Reserve space for and remember the position of the svc-file + // option. // - args[svc_file_pos] = encode_plugin_option ("svc-file", name); - exec_args[svc_file_pos] = args[svc_file_pos].c_str (); + size_t svc_file_pos (db_args.size ()); + db_args.push_back (""); + // If compiling multiple input files at once, pass them also with + // the --svc-file option. // - // - ifstream ifs; - - if (!at_once) + bool at_once (ops.at_once () && plugin_args.size () - end > 1); + if (at_once) { - ifs.open (name.c_str (), ios_base::in | ios_base::binary); - - if (!ifs.is_open ()) + if (ops.output_name ().empty ()) { - e << name << ": error: unable to open in read mode" << endl; + e << "error: --output-name required when compiling multiple " << + "input files at once (--at-once)" << endl; return 1; } - } - if (v) - { - e << "Compiling " << name << endl; - for (cstrings::const_iterator i (exec_args.begin ()); - i != exec_args.end (); ++i) - { - if (*i != 0) - e << *i << (*(i + 1) != 0 ? ' ' : '\n'); - } + for (size_t i (end); i < plugin_args.size (); ++i) + db_args.push_back ( + encode_plugin_option ("svc-file", plugin_args[i])); } - process_info pi (start_process (&exec_args[0], argv[0], false, true)); + // Create an execvp-compatible argument array. + // + typedef vector cstrings; + cstrings exec_args; + for (strings::const_iterator i (db_args.begin ()), end (db_args.end ()); + i != end; ++i) { - __gnu_cxx::stdio_filebuf fb ( - pi.out_fd, ios_base::out | ios_base::binary); - ostream os (&fb); + exec_args.push_back (i->c_str ()); + } - if (!ops.trace ()) - { - // Add the standard prologue. - // - os << "#line 1 \"\"" << endl; + exec_args.push_back ("-"); // Compile stdin. + exec_args.push_back (0); - // Make sure ODB compiler and libodb versions are compatible. - // - os << "#include " << endl - << endl - << "#if ODB_VERSION != " << ODB_VERSION << endl - << "# error incompatible ODB compiler and runtime " << - "versions" << endl - << "#endif" << endl - << endl; - - // Add ODB compiler metaprogramming tests. - // - os << "namespace odb" << endl - << "{" << endl - << "namespace compiler" << endl - << "{" << endl; + // Iterate over the input files and compile each of them. + // + for (; end < plugin_args.size (); ++end) + { + string name (at_once ? ops.output_name () : plugin_args[end]); - // operator< test, used in validator. - // - os << "template " << endl - << "bool" << endl - << "has_lt_operator (const T& x, const T& y)" << endl - << "{" << endl - << "bool r (x < y);" << endl - << "return r;" << endl - << "}" << endl; - - os << "}" << endl - << "}" << endl; - } + // Set the --svc-file option. + // + db_args[svc_file_pos] = encode_plugin_option ("svc-file", name); + exec_args[svc_file_pos] = db_args[svc_file_pos].c_str (); - // Add custom prologue if any. // - // NOTE: if you change the format, you also need to update code - // in include.cxx // - strings const& pro (ops.odb_prologue ()); - for (size_t i (0); i < pro.size (); ++i) - { - os << "#line 1 \"\"" << endl - << pro[i] - << endl; - } + ifstream ifs; - strings const& prof (ops.odb_prologue_file ()); - for (size_t i (0); i < prof.size (); ++i) + if (!at_once) { - os << "#line 1 \"\"" - << endl; - - ifstream ifs (prof[i].c_str (), ios_base::in | ios_base::binary); + ifs.open (name.c_str (), ios_base::in | ios_base::binary); if (!ifs.is_open ()) { - e << prof[i] << ": error: unable to open in read mode" << endl; - fb.close (); - wait_process (pi, argv[0]); + e << name << ": error: unable to open in read mode" << endl; return 1; } + } - if (!(os << ifs.rdbuf ())) + if (v) + { + e << "Compiling " << name << endl; + for (cstrings::const_iterator i (exec_args.begin ()); + i != exec_args.end (); ++i) { - e << prof[i] << ": error: io failure" << endl; - fb.close (); - wait_process (pi, argv[0]); - return 1; + if (*i != 0) + e << *i << (*(i + 1) != 0 ? ' ' : '\n'); } - - os << endl; } - if (at_once) - { - // Include all the input files (no need to escape). - // - os << "#line 1 \"\"" << endl; + process_info pi (start_process (&exec_args[0], argv[0], false, true)); - bool b (ops.include_with_brackets ()); - char op (b ? '<' : '"'), cl (b ? '>' : '"'); - - for (; end < plugin_args.size (); ++end) - os << "#include " << op << plugin_args[end] << cl << endl; - } - else { - // Write the synthesized translation unit to stdout. - // - os << "#line 1 \"" << escape_path (name) << "\"" << endl; + __gnu_cxx::stdio_filebuf fb ( + pi.out_fd, ios_base::out | ios_base::binary); + ostream os (&fb); - if (!(os << ifs.rdbuf ())) + if (!ops.trace ()) { - e << name << ": error: io failure" << endl; - fb.close (); - wait_process (pi, argv[0]); - return 1; + // Add the standard prologue. + // + os << "#line 1 \"\"" << endl; + + // Make sure ODB compiler and libodb versions are compatible. + // + os << "#include " << endl + << endl + << "#if ODB_VERSION != " << ODB_VERSION << endl + << "# error incompatible ODB compiler and runtime " << + "versions" << endl + << "#endif" << endl + << endl; + + // Add ODB compiler metaprogramming tests. + // + os << "namespace odb" << endl + << "{" << endl + << "namespace compiler" << endl + << "{" << endl; + + // operator< test, used in validator. + // + os << "template " << endl + << "bool" << endl + << "has_lt_operator (const T& x, const T& y)" << endl + << "{" << endl + << "bool r (x < y);" << endl + << "return r;" << endl + << "}" << endl; + + os << "}" << endl + << "}" << endl; } - // Add a new line in case the input file doesn't end with one. + // Add custom prologue if any. // - os << endl; - } + // NOTE: if you change the format, you also need to update code + // in include.cxx + // + strings const& pro (ops.odb_prologue ()); + for (size_t i (0); i < pro.size (); ++i) + { + os << "#line 1 \"\"" << endl + << pro[i] << endl; + } - // Add custom epilogue if any. - // - // NOTE: if you change the format, you also need to update code - // in include.cxx - // - strings const& epi (ops.odb_epilogue ()); - for (size_t i (0); i < epi.size (); ++i) - { - os << "#line 1 \"\"" << endl - << epi[i] - << endl; - } + strings const& prof (ops.odb_prologue_file ()); + for (size_t i (0); i < prof.size (); ++i) + { + os << "#line 1 \"\"" + << endl; - strings const& epif (ops.odb_epilogue_file ()); - for (size_t i (0); i < epif.size (); ++i) - { - os << "#line 1 \"\"" - << endl; + ifstream ifs (prof[i].c_str (), ios_base::in | ios_base::binary); - ifstream ifs (epif[i].c_str (), ios_base::in | ios_base::binary); + if (!ifs.is_open ()) + { + e << prof[i] << ": error: unable to open in read mode" << endl; + fb.close (); + wait_process (pi, argv[0]); + return 1; + } - if (!ifs.is_open ()) - { - e << epif[i] << ": error: unable to open in read mode" << endl; - fb.close (); - wait_process (pi, argv[0]); - return 1; + if (!(os << ifs.rdbuf ())) + { + e << prof[i] << ": error: io failure" << endl; + fb.close (); + wait_process (pi, argv[0]); + return 1; + } + + os << endl; } - if (!(os << ifs.rdbuf ())) + if (at_once) { - e << epif[i] << ": error: io failure" << endl; - fb.close (); - wait_process (pi, argv[0]); - return 1; + // Include all the input files (no need to escape). + // + os << "#line 1 \"\"" << endl; + + bool b (ops.include_with_brackets ()); + char op (b ? '<' : '"'), cl (b ? '>' : '"'); + + for (; end < plugin_args.size (); ++end) + os << "#include " << op << plugin_args[end] << cl << endl; } + else + { + // Write the synthesized translation unit to stdout. + // + os << "#line 1 \"" << escape_path (name) << "\"" << endl; - os << endl; - } + if (!(os << ifs.rdbuf ())) + { + e << name << ": error: io failure" << endl; + fb.close (); + wait_process (pi, argv[0]); + return 1; + } - if (!ops.trace ()) - { - // Add the standard epilogue at the end so that we see all - // the declarations. - // - os << "#line 1 \"\"" << endl; + // Add a new line in case the input file doesn't end with one. + // + os << endl; + } - // Includes for standard smart pointers. The Boost TR1 header - // may or may not delegate to the GCC implementation. In either - // case, the necessary declarations will be provided so we don't - // need to do anything. + // Add custom epilogue if any. // - os << "#include " << endl - << "#ifndef BOOST_TR1_MEMORY_HPP_INCLUDED" << endl - << "# include " << endl - << "#endif" << endl; - - // Standard wrapper traits. + // NOTE: if you change the format, you also need to update code + // in include.cxx // - os << "#include " << endl - << "#include " << endl; + strings const& epi (ops.odb_epilogue ()); + for (size_t i (0); i < epi.size (); ++i) + { + os << "#line 1 \"\"" << endl + << epi[i] << endl; + } - // Standard pointer traits. - // - os << "#include " << endl - << "#include " << endl; + strings const& epif (ops.odb_epilogue_file ()); + for (size_t i (0); i < epif.size (); ++i) + { + os << "#line 1 \"\"" + << endl; - // Standard container traits. - // - os << "#include " << endl; - } - } + ifstream ifs (epif[i].c_str (), ios_base::in | ios_base::binary); - // Filter the output stream looking for communication from the - // plugin. - // - { - __gnu_cxx::stdio_filebuf fb (pi.in_ofd, ios_base::in); - istream is (&fb); + if (!ifs.is_open ()) + { + e << epif[i] << ": error: unable to open in read mode" << endl; + fb.close (); + wait_process (pi, argv[0]); + return 1; + } - for (bool first (true); !is.eof (); ) - { - string line; - getline (is, line); + if (!(os << ifs.rdbuf ())) + { + e << epif[i] << ": error: io failure" << endl; + fb.close (); + wait_process (pi, argv[0]); + return 1; + } - if (is.fail () && !is.eof ()) + os << endl; + } + + if (!ops.trace ()) { - e << argv[0] << ": error: io failure while parsing output" << endl; - wait_process (pi, argv[0]); - return 1; + // Add the standard epilogue at the end so that we see all + // the declarations. + // + os << "#line 1 \"\"" << endl; + + // Includes for standard smart pointers. The Boost TR1 header + // may or may not delegate to the GCC implementation. In either + // case, the necessary declarations will be provided so we don't + // need to do anything. + // + os << "#include " << endl + << "#ifndef BOOST_TR1_MEMORY_HPP_INCLUDED" << endl + << "# include " << endl + << "#endif" << endl; + + // Standard wrapper traits. + // + os << "#include " << endl + << "#include " << endl; + + // Standard pointer traits. + // + os << "#include " << endl + << "#include " << endl; + + // Standard container traits. + // + os << "#include " << endl; } + } - if (line.compare (0, 9, "odb:sloc:") == 0) + // Filter the output stream looking for communication from the + // plugin. + // + { + __gnu_cxx::stdio_filebuf fb (pi.in_ofd, ios_base::in); + istream is (&fb); + + for (bool first (true); !is.eof (); ) { - if (ops.show_sloc () || ops.sloc_limit_specified ()) + string line; + getline (is, line); + + if (is.fail () && !is.eof ()) { - size_t n; - istringstream is (string (line, 9, string::npos)); + e << argv[0] << ": error: io failure while parsing output" + << endl; + wait_process (pi, argv[0]); + return 1; + } - if (!(is >> n && is.eof ())) + if (line.compare (0, 9, "odb:sloc:") == 0) + { + if (show_sloc || sloc_limit != 0) { - e << argv[0] << ": error: invalid odb:sloc value" << endl; - wait_process (pi, argv[0]); - return 1; + size_t n; + istringstream is (string (line, 9, string::npos)); + + if (!(is >> n && is.eof ())) + { + e << argv[0] << ": error: invalid odb:sloc value" << endl; + wait_process (pi, argv[0]); + return 1; + } + + sloc_total += n; } - sloc_total += n; + continue; } - continue; - } - - if (first) - first = false; - else - cout << endl; + if (first) + first = false; + else + cout << endl; - cout << line; + cout << line; + } } - } - if (!wait_process (pi, argv[0])) - return 1; - } + if (!wait_process (pi, argv[0])) + return 1; + } // End input file loop. + } // End database loop. // Handle SLOC. // - if (ops.show_sloc ()) + if (show_sloc) e << "total: " << sloc_total << endl; - if (ops.sloc_limit_specified () && ops.sloc_limit () < sloc_total) + if (sloc_limit != 0 && sloc_limit < sloc_total) { - e << argv[0] << ": error: SLOC limit of " << ops.sloc_limit () - << " lines has been exceeded" << endl; + e << argv[0] << ": error: SLOC limit of " << sloc_limit << " lines " << + "has been exceeded" << endl; - if (!ops.show_sloc ()) + if (!show_sloc) e << argv[0] << ": info: use the --show-sloc option to see the " << "current total" << endl; diff --git a/odb/option-functions.cxx b/odb/option-functions.cxx index 6b7442e..aca4943 100644 --- a/odb/option-functions.cxx +++ b/odb/option-functions.cxx @@ -3,6 +3,7 @@ // license : GNU GPL v3; see accompanying LICENSE file #include +#include // std::make_pair() #include @@ -11,6 +12,8 @@ using namespace std; void process_options (options& o) { + database db (o.database ()[0]); + // If --generate-schema-only was specified, then set --generate-schema // as well. // @@ -23,8 +26,12 @@ process_options (options& o) { set f; - switch (o.database ()) + switch (db) { + case database::common: + { + break; // No schema for common. + } case database::mssql: case database::mysql: case database::oracle: @@ -42,4 +49,56 @@ process_options (options& o) o.schema_format (f); } + + // Set default --*--file-suffix values. + // + { + database cm (database::common); + + o.odb_file_suffix ().insert (make_pair (cm, string ("-odb"))); + o.sql_file_suffix ().insert (make_pair (cm, string (""))); + o.schema_file_suffix ().insert (make_pair (cm, string ("-schema"))); + } + + if (o.multi_database () == multi_database::disabled) + { + o.odb_file_suffix ().insert (make_pair (db, string ("-odb"))); + o.sql_file_suffix ().insert (make_pair (db, string (""))); + o.schema_file_suffix ().insert (make_pair (db, string ("-schema"))); + } + else + { + o.odb_file_suffix ().insert ( + make_pair (db, string ("-odb-") + db.string ())); + o.sql_file_suffix ().insert ( + make_pair (db, string ("-") + db.string ())); + o.schema_file_suffix ().insert ( + make_pair (db, string ("-schema-") + db.string ())); + } + + // Set default --default-database value. + // + if (!o.default_database_specified ()) + { + switch (o.multi_database ()) + { + case multi_database::disabled: + { + o.default_database (db); + o.default_database_specified (true); + break; + } + case multi_database::dynamic: + { + o.default_database (database::common); + o.default_database_specified (true); + break; + } + case multi_database::static_: + { + // No default database unless explicitly specified. + break; + } + } + } } diff --git a/odb/option-parsers.hxx b/odb/option-parsers.hxx new file mode 100644 index 0000000..a8fc1f5 --- /dev/null +++ b/odb/option-parsers.hxx @@ -0,0 +1,89 @@ +// file : odb/option-parsers.hxx +// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_OPTION_PARSERS_HXX +#define ODB_OPTION_PARSERS_HXX + +#include + +#include +#include + +namespace cli +{ + template + struct parser > + { + static void + parse (database_map& m, bool& xs, scanner& s) + { + typedef database_map map; + + xs = true; + std::string o (s.next ()); + + if (s.more ()) + { + std::string ov (s.next ()); + std::string::size_type p = ov.find (':'); + + if (p != std::string::npos) + { + std::string kstr (ov, 0, p); + std::string vstr (ov, p + 1); + + // See if this prefix resolves to the database name. If not, + // assume there is no prefix. + // + database k; + std::istringstream ks (kstr); + + if (ks >> k && ks.eof ()) + { + V v = V (); + + if (!vstr.empty ()) + { + std::istringstream vs (vstr); + + if (!(vs >> v && vs.eof ())) + throw invalid_value (o, ov); + } + + m[k] = v; // Override any old value. + return; + } + } + + // No database prefix is specified which means it applies to + // all the databases. + // + V v = V (); + + if (!ov.empty ()) + { + std::istringstream vs (ov); + + if (!(vs >> v && vs.eof ())) + throw invalid_value (o, ov); + } + + // We don't want to override database-specific values, so use + // insert(). + // + m.insert (typename map::value_type (database::common, v)); + m.insert (typename map::value_type (database::mssql, v)); + m.insert (typename map::value_type (database::mysql, v)); + m.insert (typename map::value_type (database::oracle, v)); + m.insert (typename map::value_type (database::pgsql, v)); + m.insert (typename map::value_type (database::sqlite, v)); + } + else + throw missing_value (o); + } + }; +} + + +#endif // ODB_OPTION_PARSERS_HXX diff --git a/odb/option-types.cxx b/odb/option-types.cxx index ceb71db..c52935e 100644 --- a/odb/option-types.cxx +++ b/odb/option-types.cxx @@ -52,6 +52,7 @@ operator>> (istream& is, cxx_version& v) static const char* database_[] = { + "common", "mssql", "mysql", "oracle", @@ -92,6 +93,50 @@ operator<< (ostream& os, database db) } // +// multi_database +// + +static const char* multi_database_[] = +{ + "dynamic", + "static", + "disabled" +}; + +const char* multi_database:: +string () const +{ + return multi_database_[v_]; +} + +istream& +operator>> (istream& is, multi_database& db) +{ + string s; + is >> s; + + if (!is.fail ()) + { + const char** e ( + multi_database_ + sizeof (multi_database_) / sizeof (char*) - 1); + const char** i (lower_bound (multi_database_, e, s)); + + if (i != e && *i == s) + db = multi_database::value (i - multi_database_); + else + is.setstate (istream::failbit); + } + + return is; +} + +ostream& +operator<< (ostream& os, multi_database db) +{ + return os << db.string (); +} + +// // schema_format // diff --git a/odb/option-types.hxx b/odb/option-types.hxx index 66e2f28..732cd5a 100644 --- a/odb/option-types.hxx +++ b/odb/option-types.hxx @@ -5,7 +5,9 @@ #ifndef ODB_OPTION_TYPES_HXX #define ODB_OPTION_TYPES_HXX +#include #include +#include #include @@ -40,6 +42,7 @@ struct database { // Keep in alphabetic order. // + common, mssql, mysql, oracle, @@ -65,6 +68,53 @@ operator<< (std::ostream&, database); // // +template +struct database_map: std::map +{ + typedef std::map base_type; + + using base_type::operator[]; + + V const& + operator[] (database const& k) const + { + typename base_type::const_iterator i (this->find (k)); + assert (i != this->end ()); + return i->second; + } +}; + +// +// +struct multi_database +{ + enum value + { + // Keep in alphabetic order. + // + dynamic, + static_, + disabled // Special value. + }; + + multi_database (value v = disabled) : v_ (v) {} + operator value () const {return v_;} + + const char* + string () const; + +private: + value v_; +}; + +std::istream& +operator>> (std::istream&, multi_database&); + +std::ostream& +operator<< (std::ostream&, multi_database); + +// +// struct schema_format { enum value diff --git a/odb/options.cli b/odb/options.cli index cad5db7..61d8371 100644 --- a/odb/options.cli +++ b/odb/options.cli @@ -45,11 +45,26 @@ class options // // Plugin options. // - ::database --database | -d + std::vector< ::database > --database | -d { "", "Generate code for the database. Valid values are \cb{mssql}, - \cb{mysql}, \cb{oracle}, \cb{pgsql}, and \cb{sqlite}." + \cb{mysql}, \cb{oracle}, \cb{pgsql}, \cb{sqlite}, and \cb{common}." + }; + + ::multi_database --multi-database | -m = ::multi_database::disabled + { + "", + "Enable multi-database support and specify its type. Valid values + for this option are \cb{static} and \cb{dynamic}." + }; + + ::database --default-database + { + "", + "When static multi-database support is used, specify the database that + should be made the default. When dynamic multi-database support is used, + \cb{common} is always made the default database." }; bool --generate-query | -q @@ -239,14 +254,21 @@ class options generated files." }; - std::string --odb-file-suffix = "-odb" + database_map --odb-file-suffix { "", "Use instead of the default \cb{-odb} to construct the names of the generated C++ files." }; - std::string --schema-file-suffix = "-schema" + database_map --sql-file-suffix + { + "", + "Use to construct the name of the generated schema SQL file. + By default no suffix is used." + }; + + database_map --schema-file-suffix { "", "Use instead of the default \cb{-schema} to construct the name diff --git a/odb/plugin.cxx b/odb/plugin.cxx index 1d73b7f..c5dcc89 100644 --- a/odb/plugin.cxx +++ b/odb/plugin.cxx @@ -268,7 +268,7 @@ plugin_init (plugin_name_args* plugin_info, plugin_gcc_version*) cli::argv_file_scanner scan (argc, &argv[0], oi, 3); options ops (scan); assert (ops.database_specified ()); - db = ops.database (); + db = ops.database ()[0]; } profile_data pd (profile_paths_, db, "odb plugin"); diff --git a/odb/processor.cxx b/odb/processor.cxx index 0299ad5..616eddc 100644 --- a/odb/processor.cxx +++ b/odb/processor.cxx @@ -2,15 +2,1147 @@ // copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file +#include + #include +#include +#include #include +#include #include +#include #include using namespace std; +namespace +{ + struct data_member: traversal::data_member, context + { + virtual void + traverse (semantics::data_member& m) + { + if (transient (m)) + return; + + process_access (m, "get"); + process_access (m, "set"); + } + + // + // Process member access expressions. + // + + enum found_type + { + found_none, + found_some, // Found something but keep looking for a better one. + found_best + }; + + // Check if a function is a suitable accessor for this member. + // + found_type + check_accessor (semantics::data_member& m, + tree f, + string const& n, + member_access& ma, + bool strict) + { + // Must be const. + // + if (!DECL_CONST_MEMFUNC_P (f)) + return found_none; + + // Accessor is a function with no arguments (other than 'this'). + // + if (DECL_CHAIN (DECL_ARGUMENTS (f)) != NULL_TREE) + return found_none; + + // Note that to get the return type we have to use + // TREE_TYPE(TREE_TYPE()) and not DECL_RESULT, as + // suggested in the documentation. + // + tree r (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f)))); + int tc (TREE_CODE (r)); + + // In the strict mode make sure the function returns for non-array + // types a value or a (const) reference to the member type and for + // array types a (const) pointer to element type. In the lax mode + // we just check that the return value is not void. + // + if (strict) + { + semantics::type& t (utype (m)); + semantics::array* ar (dynamic_cast (&t)); + + if (ar != 0 && tc != POINTER_TYPE) + return found_none; + + tree bt (ar != 0 || tc == REFERENCE_TYPE ? TREE_TYPE (r) : r); + tree bt_mv (TYPE_MAIN_VARIANT (bt)); + + if ((ar != 0 ? ar->base_type () : t).tree_node () != bt_mv) + return found_none; + } + else if (r == void_type_node) + return found_none; + + cxx_tokens& e (ma.expr); + e.push_back (cxx_token (0, CPP_KEYWORD, "this")); + e.push_back (cxx_token (0, CPP_DOT)); + e.push_back (cxx_token (0, CPP_NAME, n)); + e.push_back (cxx_token (0, CPP_OPEN_PAREN, n)); + e.push_back (cxx_token (0, CPP_CLOSE_PAREN, n)); + + // See if it returns by value. + // + ma.by_value = (tc != REFERENCE_TYPE && tc != POINTER_TYPE); + + return found_best; + } + + // Check if a function is a suitable modifier for this member. + // + found_type + check_modifier (semantics::data_member& m, + tree f, + string const& n, + member_access& ma, + bool strict) + { + tree a (DECL_ARGUMENTS (f)); + a = DECL_CHAIN (a); // Skip this. + + // For a modifier, it can either be a function that returns a non- + // const reference (or non-const pointer, in case the member is an + // array) or a by-value modifier that sets a new value. If both are + // available, we prefer the former for efficiency. + // + cxx_tokens& e (ma.expr); + semantics::type& t (utype (m)); + semantics::array* ar (dynamic_cast (&t)); + + if (a == NULL_TREE) + { + // Note that to get the return type we have to use + // TREE_TYPE(TREE_TYPE()) and not DECL_RESULT, as + // suggested in the documentation. + // + tree r (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f)))); + int tc (TREE_CODE (r)); + + // By-reference modifier. Should return a reference or a pointer. + // + if (tc != (ar != 0 ? POINTER_TYPE : REFERENCE_TYPE)) + return found_none; + + // The base type should not be const and, in strict mode, should + // match the member type. + // + tree bt (TREE_TYPE (r)); + + if (CP_TYPE_CONST_P (bt)) + return found_none; + + tree bt_mv (TYPE_MAIN_VARIANT (bt)); + + if (strict && (ar != 0 ? ar->base_type () : t).tree_node () != bt_mv) + return found_none; + + e.clear (); // Could contain by value modifier. + e.push_back (cxx_token (0, CPP_KEYWORD, "this")); + e.push_back (cxx_token (0, CPP_DOT)); + e.push_back (cxx_token (0, CPP_NAME, n)); + e.push_back (cxx_token (0, CPP_OPEN_PAREN, n)); + e.push_back (cxx_token (0, CPP_CLOSE_PAREN, n)); + + return found_best; + } + // Otherwise look for a by value modifier, which is a function + // with a single argument. + // + else if (DECL_CHAIN (a) == NULL_TREE) + { + // In the lax mode any function with a single argument works + // for us. And we don't care what it returns. + // + if (strict) + { + // In the strict mode make sure the argument matches the + // member. This is exactly the same logic as in accessor + // with regards to arrays, references, etc. + // + tree at (TREE_TYPE (a)); + int tc (TREE_CODE (at)); + + if (ar != 0 && tc != POINTER_TYPE) + return found_none; + + tree bt (ar != 0 || tc == REFERENCE_TYPE ? TREE_TYPE (at) : at); + tree bt_mv (TYPE_MAIN_VARIANT (bt)); + + if ((ar != 0 ? ar->base_type () : t).tree_node () != bt_mv) + return found_none; + } + + if (e.empty ()) + { + e.push_back (cxx_token (0, CPP_KEYWORD, "this")); + e.push_back (cxx_token (0, CPP_DOT)); + e.push_back (cxx_token (0, CPP_NAME, n)); + e.push_back (cxx_token (0, CPP_OPEN_PAREN, n)); + e.push_back (cxx_token (0, CPP_QUERY)); + e.push_back (cxx_token (0, CPP_CLOSE_PAREN, n)); + + // Continue searching in case there is version that returns a + // non-const reference which we prefer for efficiency. + // + return found_some; + } + else + return found_none; // We didn't find anything better. + } + + return found_none; + } + + void + process_access (semantics::data_member& m, std::string const& k) + { + bool virt (m.count ("virtual")); + + // Ignore certain special virtual members. + // + if (virt && (m.count ("polymorphic-ref") || m.count ("discriminator"))) + return; + + char const* kind (k == "get" ? "accessor" : "modifier"); + semantics::class_& c (dynamic_cast (m.scope ())); + + // If we don't have an access expression, try to come up with + // one. + // + if (!m.count (k)) + { + found_type found (found_none); + semantics::access const& a (m.named ().access ()); + member_access& ma (m.set (k, member_access (m.location (), true))); + + // If this member is not virtual and is either public or if we + // are a friend of this class, then go for the member directly. + // + if (!virt && (a == semantics::access::public_ || + c.get ("friend"))) + { + ma.expr.push_back (cxx_token (0, CPP_KEYWORD, "this")); + ma.expr.push_back (cxx_token (0, CPP_DOT)); + ma.expr.push_back (cxx_token (0, CPP_NAME, m.name ())); + found = found_best; + } + + // Otherwise, try to find a suitable accessor/modifier. + // + + // First try the original name. If that doesn't produce anything, + // then try the public name. + // + bool t (k == "get" + ? options.accessor_regex_trace () + : options.modifier_regex_trace ()); + regex_mapping const& re ( + k == "get" ? accessor_regex : modifier_regex); + + for (unsigned short j (0); found != found_best && j != 2; ++j) + { + string b (j == 0 ? m.name () : public_name (m, false)); + + // Skip the second pass if original and public names are the same. + // + if (j == 1 && b == m.name ()) + continue; + + if (t) + cerr << kind << (j == 0 ? " original" : " public") + << " name '" << b << "'" << endl; + + for (regex_mapping::const_iterator i (re.begin ()); + found != found_best && i != re.end (); + ++i) + { + if (t) + cerr << "try: '" << i->regex () << "' : "; + + if (!i->match (b)) + { + if (t) + cerr << '-' << endl; + continue; + } + + string n (i->replace (b)); + + if (t) + cerr << "'" << n << "' : "; + + tree decl ( + lookup_qualified_name ( + c.tree_node (), get_identifier (n.c_str ()), false, false)); + + if (decl == error_mark_node || TREE_CODE (decl) != BASELINK) + { + if (t) + cerr << '-' << endl; + continue; + } + + // OVL_* macros work for both FUNCTION_DECL and OVERLOAD. + // + for (tree o (BASELINK_FUNCTIONS (decl)); + o != 0; + o = OVL_NEXT (o)) + { + tree f (OVL_CURRENT (o)); + + // We are only interested in public non-static member + // functions. Note that TREE_PUBLIC() returns something + // other than what we need. + // + if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (f) || + TREE_PRIVATE (f) || TREE_PROTECTED (f)) + continue; + + found_type r (k == "get" + ? check_accessor (m, f, n, ma, true) + : check_modifier (m, f, n, ma, true)); + + if (r != found_none) + { + // Update the location of the access expression to point + // to this function. + // + ma.loc = location (DECL_SOURCE_LOCATION (f)); + found = r; + } + } + + if (t) + cerr << (found != found_none ? '+' : '-') << endl; + } + } + + // If that didn't work then the generated code won't be able + // to access this member. + // + if (found == found_none) + { + location const& l (m.location ()); + + if (virt) + { + error (l) << "no suitable " << kind << " function could be " + << "automatically found for virtual data member '" + << m.name () << "'" << endl; + + info (l) << "use '#pragma db " << k << "' to explicitly " + << "specify the " << kind << " function or " + << "expression" << endl; + } + else + { + error (l) << "data member '" << m.name () << "' is " + << a.string () << " and no suitable " << kind + << " function could be automatically found" << endl; + + info (l) << "consider making class 'odb::access' a friend of " + << "class '" << class_name (c) << "'" << endl; + + info (l) << "or use '#pragma db " << k << "' to explicitly " + << "specify the " << kind << " function or " + << "expression" << endl; + } + + throw operation_failed (); + } + } + + member_access& ma (m.get (k)); + cxx_tokens& e (ma.expr); + + // If it is just a name, resolve it and convert to an appropriate + // expression. + // + if (e.size () == 1 && e.back ().type == CPP_NAME) + { + string n (e.back ().literal); + e.clear (); + + tree decl ( + lookup_qualified_name ( + c.tree_node (), get_identifier (n.c_str ()), false, false)); + + if (decl == error_mark_node) + { + error (ma.loc) << "unable to resolve data member or function " + << "name '" << n << "'" << endl; + throw operation_failed (); + } + + switch (TREE_CODE (decl)) + { + case FIELD_DECL: + { + e.push_back (cxx_token (0, CPP_KEYWORD, "this")); + e.push_back (cxx_token (0, CPP_DOT)); + e.push_back (cxx_token (0, CPP_NAME, n)); + break; + } + case BASELINK: + { + // OVL_* macros work for both FUNCTION_DECL and OVERLOAD. + // + for (tree o (BASELINK_FUNCTIONS (decl)); + o != 0; + o = OVL_NEXT (o)) + { + tree f (OVL_CURRENT (o)); + + // We are only interested in non-static member functions. + // + if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (f)) + continue; + + if ((k == "get" + ? check_accessor (m, f, n, ma, false) + : check_modifier (m, f, n, ma, false)) == found_best) + break; + } + + if (e.empty ()) + { + error (ma.loc) << "unable to find suitable " << kind + << " function '" << n << "'" << endl; + throw operation_failed (); + } + break; + } + default: + { + error (ma.loc) << "name '" << n << "' does not refer to a data " + << "member or function" << endl; + throw operation_failed (); + } + } + } + + // If there is no 'this' keyword, then add it as a prefix. + // + { + bool t (false); + for (cxx_tokens::iterator i (e.begin ()); i != e.end (); ++i) + { + if (i->type == CPP_KEYWORD && i->literal == "this") + { + t = true; + break; + } + } + + if (!t) + { + e.insert (e.begin (), cxx_token (0, CPP_DOT)); + e.insert (e.begin (), cxx_token (0, CPP_KEYWORD, "this")); + } + } + + // Check that there is no placeholder in the accessor expression. + // + if (k == "get" && ma.placeholder ()) + { + error (ma.loc) << "(?) placeholder in the accessor expression" + << endl; + throw operation_failed (); + } + + // Check that the member type is default-constructible if we + // have a by value modifier. + // + if (k == "set" && ma.placeholder ()) + { + semantics::class_* c (dynamic_cast (&utype (m))); + + // Assume all other types are default-constructible. + // + if (c != 0) + { + // If this type is a class template instantiation, then make + // sure it is instantiated. While types used in real members + // will be instantiated, this is not necessarily the case for + // virtual members. Without the instantiation we won't be able + // to detect whether the type has the default ctor. + // + // It would have been cleaner to do it in post_process_pragmas() + // but there we don't yet know whether we need the default ctor. + // And it is a good idea not to require instantiability unless + // we really need it. + // + tree type (c->tree_node ()); + + if (!COMPLETE_TYPE_P (type) && + CLASSTYPE_TEMPLATE_INSTANTIATION (type)) + { + // Reset input location so that we get nice diagnostics in + // case of an error. Use the location of the virtual pragma. + // + location_t loc (m.get ("virtual-location")); + input_location = loc; + + if (instantiate_class_template (type) == error_mark_node || + errorcount != 0 || + !COMPLETE_TYPE_P (type)) + { + error (loc) << "unable to instantiate virtual data member " << + "type" << endl; + throw operation_failed (); + } + } + + if (!c->default_ctor ()) + { + error (ma.loc) << "modifier expression requires member type " << + "to be default-constructible" << endl; + throw operation_failed (); + } + } + } + } + }; + + struct class_: traversal::class_, context + { + class_ () + : std_string_ (0), std_string_hint_ (0), access_ (0) + { + *this >> member_names_ >> member_; + + // Resolve the std::string type node. + // + using semantics::scope; + + for (scope::names_iterator_pair ip (unit.find ("std")); + ip.first != ip.second; ++ip.first) + { + if (scope* ns = dynamic_cast (&ip.first->named ())) + { + scope::names_iterator_pair jp (ns->find ("string")); + + if (jp.first != jp.second) + { + std_string_ = dynamic_cast ( + &jp.first->named ()); + std_string_hint_ = &*jp.first; + break; + } + } + } + + assert (std_string_ != 0); // No std::string? + + // Resolve odb::access, if any. + // + tree odb = lookup_qualified_name ( + global_namespace, get_identifier ("odb"), false, false); + + if (odb != error_mark_node) + { + access_ = lookup_qualified_name ( + odb, get_identifier ("access"), true, false); + + access_ = (access_ != error_mark_node ? TREE_TYPE (access_) : 0); + } + } + + virtual void + traverse (type& c) + { + class_kind_type k (class_kind (c)); + + if (k == class_other) + return; + + // Check if odb::access is a friend of this class. + // + c.set ("friend", access_ != 0 && is_friend (c.tree_node (), access_)); + + // Assign pointer. + // + if (k == class_object || k == class_view) + assign_pointer (c); + + if (k == class_object) + traverse_object (c); + + names (c); + } + + // + // Object. + // + + virtual void + traverse_object (type& c) + { + semantics::class_* poly_root (polymorphic (c)); + + // Determine whether it is a session object. + // + if (!c.count ("session")) + { + // If this is a derived class in a polymorphic hierarchy, + // then it should have the same session value as the root. + // + if (poly_root != 0 && poly_root != &c) + c.set ("session", session (*poly_root)); + else + { + // See if any of the namespaces containing this class specify + // the session value. + // + bool found (false); + for (semantics::scope* s (&c.scope ());; s = &s->scope_ ()) + { + using semantics::namespace_; + + namespace_* ns (dynamic_cast (s)); + + if (ns == 0) + continue; // Some other scope. + + if (ns->extension ()) + ns = &ns->original (); + + if (ns->count ("session")) + { + c.set ("session", ns->get ("session")); + found = true; + break; + } + + if (ns->global_scope ()) + break; + } + + // If still not found, then use the default value. + // + if (!found) + c.set ("session", options.generate_session ()); + } + } + + if (session (c)) + features.session_object = true; + + if (poly_root != 0) + { + using namespace semantics; + + semantics::data_member& idm (*id_member (*poly_root)); + + if (poly_root != &c) + { + // If we are a derived class in the polymorphic persistent + // class hierarchy, then add a synthesized virtual pointer + // member that points back to the root. + // + path const& f (idm.file ()); + size_t l (idm.line ()), col (idm.column ()); + + semantics::data_member& m ( + unit.new_node (f, l, col, tree (0))); + m.set ("virtual", true); + + // Make it the first member in the class. + // + node_position np (c, c.names_end ()); + unit.new_edge ( + np, m, idm.name (), access::public_); + + // Use the raw pointer as this member's type. + // + if (!poly_root->pointed_p ()) + { + // Create the pointer type in the graph. The pointer node + // in GCC seems to always be present, even if not explicitly + // used in the translation unit. + // + tree t (poly_root->tree_node ()); + tree ptr (TYPE_POINTER_TO (t)); + assert (ptr != 0); + ptr = TYPE_MAIN_VARIANT (ptr); + pointer& p (unit.new_node (f, l, col, ptr)); + unit.insert (ptr, p); + unit.new_edge (p, *poly_root); + assert (poly_root->pointed_p ()); + } + + unit.new_edge (m, poly_root->pointed ().pointer ()); + + // Mark it as a special kind of id. + // + m.set ("id", true); + m.set ("polymorphic-ref", true); + } + else + { + // If we are a root of the polymorphic persistent class hierarchy, + // then add a synthesized virtual member for the discriminator. + // Use the location of the polymorphic pragma as the location of + // this member. + // + location_t loc (c.get ("polymorphic-location")); + semantics::data_member& m ( + unit.new_node ( + path (LOCATION_FILE (loc)), + LOCATION_LINE (loc), + LOCATION_COLUMN (loc), + tree (0))); + m.set ("virtual", true); + + // Insert it after the id member (or first if this id comes + // from reuse-base). + // + node_position np ( + c, c.find (idm.named ())); + unit.new_edge ( + np, m, "typeid_", access::public_); + + belongs& edge (unit.new_edge (m, *std_string_)); + edge.hint (*std_string_hint_); + + m.set ("readonly", true); + m.set ("discriminator", true); + + c.set ("discriminator", &m); + } + } + } + + void + assign_pointer (type& c) + { + location_t loc (0); // Pragma location, or 0 if not used. + + try + { + string ptr; + string const& type (class_fq_name (c)); + + tree decl (0); // Resolved template node. + string decl_name; // User-provided template name. + tree resolve_scope (0); // Scope in which we resolve names. + + class_pointer const* cp (0); + bool cp_template (false); + + if (c.count ("pointer")) + { + cp = &c.get ("pointer"); + } + // If we are a derived type in polymorphic hierarchy, then use + // our root's pointer type by default. + // + else if (semantics::class_* r = polymorphic (c)) + { + if (&c != r && r->count ("pointer-template")) + cp = r->get ("pointer-template"); + } + + if (cp != 0) + { + string const& p (cp->name); + + if (p == "*") + { + ptr = type + "*"; + cp_template = true; + } + else if (p[p.size () - 1] == '*') + ptr = p; + else if (p.find ('<') != string::npos) + { + // Template-id. + // + ptr = p; + decl_name.assign (p, 0, p.find ('<')); + } + else + { + // This is not a template-id. Resolve it and see if it is a + // template or a type. + // + decl = resolve_name (p, cp->scope, true); + int tc (TREE_CODE (decl)); + + if (tc == TYPE_DECL) + { + ptr = p; + + // This can be a typedef'ed alias for a TR1 template-id. + // + if (tree ti = TYPE_TEMPLATE_INFO (TREE_TYPE (decl))) + { + decl = TI_TEMPLATE (ti); // DECL_TEMPLATE + + // Get to the most general template declaration. + // + while (DECL_TEMPLATE_INFO (decl)) + decl = DECL_TI_TEMPLATE (decl); + } + else + decl = 0; // Not a template. + } + else if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl)) + { + ptr = p + "< " + type + " >"; + decl_name = p; + cp_template = true; + } + else + { + error (cp->loc) + << "name '" << p << "' specified with db pragma pointer " + << "does not name a type or a template" << endl; + + throw operation_failed (); + } + } + + // Resolve scope is the scope of the pragma. + // + resolve_scope = cp->scope; + loc = cp->loc; + } + else + { + // See if any of the namespaces containing this class specify + // a pointer. + // + for (semantics::scope* s (&c.scope ());; s = &s->scope_ ()) + { + using semantics::namespace_; + + namespace_* ns (dynamic_cast (s)); + + if (ns == 0) + continue; // Some other scope. + + if (ns->extension ()) + ns = &ns->original (); + + if (!ns->count ("pointer")) + { + if (ns->global_scope ()) + break; + else + continue; + } + + cp = &ns->get ("pointer"); + string const& p (cp->name); + + // Namespace-specified pointer can only be '*' or are template. + // + if (p == "*") + ptr = type + "*"; + else if (p[p.size () - 1] == '*') + { + error (cp->loc) + << "name '" << p << "' specified with db pragma pointer " + << "at namespace level cannot be a raw pointer" << endl; + } + else if (p.find ('<') != string::npos) + { + error (cp->loc) + << "name '" << p << "' specified with db pragma pointer " + << "at namespace level cannot be a template-id" << endl; + } + else + { + // Resolve this name and make sure it is a template. + // + decl = resolve_name (p, cp->scope, true); + int tc (TREE_CODE (decl)); + + if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl)) + { + ptr = p + "< " + type + " >"; + decl_name = p; + } + else + { + error (cp->loc) + << "name '" << p << "' specified with db pragma pointer " + << "does not name a template" << endl; + } + } + + if (ptr.empty ()) + throw operation_failed (); + + cp_template = true; + + // Resolve scope is the scope of the pragma. + // + resolve_scope = cp->scope; + loc = cp->loc; + break; + } + + // Use the default pointer. + // + if (ptr.empty ()) + { + string const& p (options.default_pointer ()); + + if (p == "*") + ptr = type + "*"; + else + { + ptr = p + "< " + type + " >"; + decl_name = p; + } + + // Resolve scope is the scope of the class. + // + resolve_scope = c.scope ().tree_node (); + } + } + + // If this class is a root of a polymorphic hierarchy, then cache + // the pointer template so that we can use it for derived classes. + // + if (cp != 0 && cp_template && polymorphic (c) == &c) + c.set ("pointer-template", cp); + + // Check if we are using TR1. + // + if (decl != 0 || !decl_name.empty ()) + { + bool& tr1 (features.tr1_pointer); + bool& boost (features.boost_pointer); + + // First check the user-supplied name. + // + tr1 = tr1 + || decl_name.compare (0, 8, "std::tr1") == 0 + || decl_name.compare (0, 10, "::std::tr1") == 0; + + // If there was no match, also resolve the name since it can be + // a using-declaration for a TR1 template. + // + if (!tr1) + { + if (decl == 0) + decl = resolve_name (decl_name, resolve_scope, false); + + if (TREE_CODE (decl) != TEMPLATE_DECL || ! + DECL_CLASS_TEMPLATE_P (decl)) + { + // This is only checked for the --default-pointer option. + // + error (c.file (), c.line (), c.column ()) + << "name '" << decl_name << "' specified with the " + << "--default-pointer option does not name a class " + << "template" << endl; + + throw operation_failed (); + } + + string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER)); + + // In case of a boost TR1 implementation, we cannot distinguish + // between the boost:: and std::tr1:: usage since the latter is + // just a using-declaration for the former. + // + tr1 = tr1 + || n.compare (0, 8, "std::tr1") == 0 + || n.compare (0, 10, "::std::tr1") == 0; + + boost = boost + || n.compare (0, 17, "boost::shared_ptr") == 0 + || n.compare (0, 19, "::boost::shared_ptr") == 0; + } + } + + // Fully-qualify all the unqualified components of the name. + // + try + { + lex_.start (ptr); + ptr.clear (); + + string t; + bool punc (false); + bool scoped (false); + + for (cpp_ttype tt (lex_.next (t)); + tt != CPP_EOF; + tt = lex_.next (t)) + { + if (punc && tt > CPP_LAST_PUNCTUATOR) + ptr += ' '; + + punc = false; + + switch (static_cast (tt)) + { + case CPP_LESS: + { + ptr += "< "; + break; + } + case CPP_GREATER: + { + ptr += " >"; + break; + } + case CPP_COMMA: + { + ptr += ", "; + break; + } + case CPP_NAME: + { + // If the name was not preceeded with '::', look it + // up in the pragmas's scope and add the qualifer. + // + if (!scoped) + { + tree decl (resolve_name (t, resolve_scope, false)); + tree scope (CP_DECL_CONTEXT (decl)); + + if (scope != global_namespace) + { + ptr += "::"; + ptr += decl_as_string (scope, TFF_PLAIN_IDENTIFIER); + } + + ptr += "::"; + } + + ptr += t; + punc = true; + break; + } + case CPP_KEYWORD: + case CPP_NUMBER: + { + ptr += t; + punc = true; + break; + } + default: + { + ptr += t; + break; + } + } + + scoped = (tt == CPP_SCOPE); + } + } + catch (cxx_lexer::invalid_input const&) + { + throw operation_failed (); + } + + c.set ("object-pointer", ptr); + } + catch (invalid_name const& ex) + { + if (loc != 0) + error (loc) + << "name '" << ex.name () << "' specified with db pragma " + << "pointer is invalid" << endl; + else + error (c.file (), c.line (), c.column ()) + << "name '" << ex.name () << "' specified with the " + << "--default-pointer option is invalid" << endl; + + + throw operation_failed (); + } + catch (unable_to_resolve const& ex) + { + if (loc != 0) + error (loc) + << "unable to resolve name '" << ex.name () << "' specified " + << "with db pragma pointer" << endl; + else + error (c.file (), c.line (), c.column ()) + << "unable to resolve name '" << ex.name () << "' specified " + << "with the --default-pointer option" << endl; + + throw operation_failed (); + } + } + + private: + struct invalid_name + { + invalid_name (string const& n): name_ (n) {} + + string const& + name () const {return name_;} + + private: + string name_; + }; + + typedef lookup::unable_to_resolve unable_to_resolve; + + tree + resolve_name (string const& qn, tree scope, bool is_type) + { + try + { + string tl; + tree tn; + cpp_ttype tt, ptt; + + nlex_.start (qn); + tt = nlex_.next (tl, &tn); + + string name; + return lookup::resolve_scoped_name ( + nlex_, tt, tl, tn, ptt, scope, name, is_type); + } + catch (cxx_lexer::invalid_input const&) + { + throw invalid_name (qn); + } + catch (lookup::invalid_name const&) + { + throw invalid_name (qn); + } + } + + private: + data_member member_; + traversal::names member_names_; + + cxx_string_lexer lex_; + cxx_string_lexer nlex_; // Nested lexer. + + semantics::type* std_string_; + semantics::names* std_string_hint_; + + tree access_; // odb::access node. + }; +} + void processor:: process (options const& ops, features& f, @@ -19,10 +1151,49 @@ process (options const& ops, { try { - // Process types. - // auto_ptr ctx (create_context (cerr, unit, ops, f, 0)); - relational::process (); + + // Common processing. + // + { + traversal::unit unit; + traversal::defines unit_defines; + typedefs unit_typedefs (true); + traversal::namespace_ ns; + class_ c; + + 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 (ctx->unit); + } + + // Database-specific processing. + // + switch (ops.database ()[0]) + { + case database::common: + { + break; + } + case database::mssql: + case database::mysql: + case database::oracle: + case database::pgsql: + case database::sqlite: + { + relational::process (); + break; + } + } } catch (operation_failed const&) { diff --git a/odb/profile.cxx b/odb/profile.cxx index fedd7a9..03bf8a4 100644 --- a/odb/profile.cxx +++ b/odb/profile.cxx @@ -12,8 +12,6 @@ using namespace std; - - static bool exist (profile_data::path const& p) { @@ -64,6 +62,12 @@ profile_search (char const* prof, void* arg) if (i == end) { + // Ignore the case where we didn't find the profile and this is the + // common database. + // + if (pd->db == database::common) + return string (); + cerr << pd->name << ": error: unable to locate options file for profile '" << prof << "'" << endl; throw profile_failure (); diff --git a/odb/relational/common.hxx b/odb/relational/common.hxx index 26e8197..b951d15 100644 --- a/odb/relational/common.hxx +++ b/odb/relational/common.hxx @@ -336,7 +336,7 @@ namespace relational static B* create (B const& prototype) { - database db (context::current ().options.database ()); + database db (context::current ().options.database ()[0]); if (map_ != 0) { diff --git a/odb/relational/header.cxx b/odb/relational/header.cxx index c6fc9a5..1295106 100644 --- a/odb/relational/header.cxx +++ b/odb/relational/header.cxx @@ -13,15 +13,14 @@ traverse_object (type& c) using semantics::data_member; data_member* id (id_member (c)); - bool auto_id (id ? id->count ("auto") : false); - bool base_id (id ? &id->scope () != &c : false); // Comes from base. + bool auto_id (id && auto_ (*id)); + bool base_id (id && &id->scope () != &c); // Comes from base. data_member* optimistic (context::optimistic (c)); type* poly_root (polymorphic (c)); bool poly (poly_root != 0); bool poly_derived (poly && poly_root != &c); - type* poly_base (poly_derived ? &polymorphic_base (c) : 0); data_member* discriminator (poly ? context::discriminator (*poly_root) : 0); bool abst (abstract (c)); @@ -33,196 +32,6 @@ traverse_object (type& c) os << "// " << class_name (c) << endl << "//" << endl; - // class_traits - // - os << "template <>" << endl - << "struct class_traits< " << type << " >" - << "{" - << "static const class_kind kind = class_object;" - << "};"; - - // object_traits - // - os << "template <>" << endl - << "class access::object_traits< " << type << " >" - << "{" - << "public:" << endl; - - // object_type & pointer_type - // - os << "typedef " << type << " object_type;" - << "typedef " << c.get ("object-pointer") << " pointer_type;" - << "typedef odb::pointer_traits pointer_traits;" - << endl; - - // polymorphic, root_type, base_type, etc. - // - os << "static const bool polymorphic = " << (poly ? "true" : "false") << ";" - << endl; - - if (poly) - { - os << "typedef " << class_fq_name (*poly_root) << " root_type;"; - - if (poly_derived) - { - os << "typedef " << class_fq_name (*poly_base) << " base_type;" - << "typedef object_traits::discriminator_type " << - "discriminator_type;" - << "typedef polymorphic_concrete_info info_type;"; - - if (abst) - os << "typedef polymorphic_abstract_info " << - "abstract_info_type;"; - - // Calculate our hierarchy depth (number of classes). - // - size_t depth (polymorphic_depth (c)); - - os << endl - << "static const std::size_t depth = " << depth << "UL;"; - } - else - { - semantics::names* hint; - semantics::type& t (utype (*discriminator, hint)); - - os << "typedef " << t.fq_name (hint) << " discriminator_type;" - << "typedef polymorphic_map map_type;" - << "typedef polymorphic_concrete_info info_type;"; - - if (abst) - os << "typedef polymorphic_abstract_info " << - "abstract_info_type;"; - - os << endl - << "static const std::size_t depth = 1UL;"; - } - - os << endl; - } - - // id_type, version_type, etc. - // - if (id != 0) - { - if (base_id) - { - semantics::class_& b ( - dynamic_cast (id->scope ())); - string const& type (class_fq_name (b)); - - os << "typedef object_traits< " << type << " >::id_type id_type;"; - - if (optimistic != 0) - os << "typedef object_traits< " << type << " >::version_type " << - "version_type;"; - - os << endl; - - if (poly_derived) - os << "static const bool auto_id = false;"; - else - os << "static const bool auto_id = object_traits< " << type << - " >::auto_id;"; - } - else - { - { - semantics::names* hint; - semantics::type& t (utype (*id, hint)); - os << "typedef " << t.fq_name (hint) << " id_type;"; - } - - if (optimistic != 0) - { - semantics::names* hint; - semantics::type& t (utype (*optimistic, hint)); - os << "typedef " << t.fq_name (hint) << " version_type;"; - } - - os << endl - << "static const bool auto_id = " << (auto_id ? "true;" : "false;"); - } - - os << endl; - } - else if (!reuse_abst) - { - // Object without id. - // - os << "typedef void id_type;" - << endl - << "static const bool auto_id = false;" - << endl; - } - - // abstract - // - os << "static const bool abstract = " << (abst ? "true" : "false") << ";" - << endl; - - // id () - // - if (id != 0 || !reuse_abst) - { - // We want to generate a dummy void id() accessor even if this - // object has no id to help us in the runtime. This way we can - // write generic code that will work for both void and non-void - // ids. - // - os << "static id_type" << endl - << "id (const object_type&);" - << endl; - } - - if (!reuse_abst) - { - // Cache traits typedefs. - // - if (id == 0) - { - os << "typedef" << endl - << "no_id_pointer_cache_traits" << endl - << "pointer_cache_traits;" - << endl - << "typedef" << endl - << "no_id_reference_cache_traits" << endl - << "reference_cache_traits;" - << endl; - } - else - { - char const* p (session (c) ? "odb::" : "no_op_"); - - if (poly_derived) - { - os << "typedef" << endl - << p << "pointer_cache_traits<" << - "object_traits::pointer_type>" << endl - << "pointer_cache_traits;" - << endl - << "typedef" << endl - << p << "reference_cache_traits" << endl - << "reference_cache_traits;" - << endl; - } - else - { - os << "typedef" << endl - << p << "pointer_cache_traits" << endl - << "pointer_cache_traits;" - << endl - << "typedef" << endl - << p << "reference_cache_traits" << endl - << "reference_cache_traits;" - << endl; - } - } - } - - os << "};"; - // pointer_query_columns & query_columns // if (options.generate_query ()) @@ -584,16 +393,6 @@ traverse_object (type& c) // Functions (concrete). // - // callback () - // - os << "static void" << endl - << "callback (database&, object_type&, callback_event);" - << endl; - - os << "static void" << endl - << "callback (database&, const object_type&, callback_event);" - << endl; - // persist () // os << "static void" << endl @@ -814,13 +613,17 @@ traverse_object (type& c) // // Note that it is not generated for reuse-abstract classes. // - os << "template <>" << endl - << "class access::object_traits_impl< " << type << ", " << - "id_default >:" << endl - << " public access::object_traits_impl< " << type << ", " << - "id_" << db << " >" - << "{" - << "};"; + if (options.default_database_specified () && + options.default_database () == db) + { + os << "template <>" << endl + << "class access::object_traits_impl< " << type << ", " << + "id_default >:" << endl + << " public access::object_traits_impl< " << type << ", " << + "id_" << db << " >" + << "{" + << "};"; + } } void relational::header::class1:: @@ -831,28 +634,6 @@ traverse_view (type& c) os << "// " << class_name (c) << endl << "//" << endl; - // class_traits - // - os << "template <>" << endl - << "struct class_traits< " << type << " >" - << "{" - << "static const class_kind kind = class_view;" - << "};"; - - // view_traits - // - os << "template <>" << endl - << "class access::view_traits< " << type << " >" - << "{" - << "public:" << endl; - - // view_type & pointer_type - // - os << "typedef " << type << " view_type;" - << "typedef " << c.get ("object-pointer") << " pointer_type;"; - - os << "};"; - // view_traits_impl // os << "template <>" << endl @@ -933,12 +714,6 @@ traverse_view (type& c) // Functions. // - // callback () - // - os << "static void" << endl - << "callback (database&, view_type&, callback_event);" - << endl; - // query () // if (!options.omit_unprepared ()) @@ -963,12 +738,17 @@ traverse_view (type& c) // view_traits_impl< , id_default> // - os << "template <>" << endl - << "class access::view_traits_impl< " << type << ", id_default >:" << endl - << " public access::view_traits_impl< " << type << ", " << - "id_" << db << " >" - << "{" - << "};"; + if (options.default_database_specified () && + options.default_database () == db) + { + os << "template <>" << endl + << "class access::view_traits_impl< " << type << ", " << + "id_default >:" << endl + << " public access::view_traits_impl< " << type << ", " << + "id_" << db << " >" + << "{" + << "};"; + } } void relational::header::class1:: @@ -980,12 +760,6 @@ traverse_composite (type& c) << "//" << endl; os << "template <>" << endl - << "struct class_traits< " << type << " >" - << "{" - << "static const class_kind kind = class_composite;" - << "};"; - - os << "template <>" << endl << "class access::composite_value_traits< " << type << ", " << "id_" << db << " >" << "{" diff --git a/odb/relational/header.hxx b/odb/relational/header.hxx index 75cee16..c0e1e89 100644 --- a/odb/relational/header.hxx +++ b/odb/relational/header.hxx @@ -1233,8 +1233,7 @@ namespace relational virtual void generate () { - os << "#include " << endl - << "#include " << endl; + os << "#include " << endl; if (options.generate_query ()) os << "#include " << endl; diff --git a/odb/relational/inline.hxx b/odb/relational/inline.hxx index 7e70ab1..466e348 100644 --- a/odb/relational/inline.hxx +++ b/odb/relational/inline.hxx @@ -14,68 +14,6 @@ namespace relational namespace inline_ { // - // - struct callback_calls: traversal::class_, virtual context - { - typedef callback_calls base; - - callback_calls () - { - *this >> inherits_ >> *this; - } - - callback_calls (callback_calls const&) - : root_context (), //@@ -Wextra - context () - { - *this >> inherits_ >> *this; - } - - virtual void - traverse (type& c, bool constant) - { - const_ = constant; - traverse (c); - } - - virtual void - traverse (type& c) - { - bool obj (object (c)); - - // Ignore transient bases. - // - if (!(obj || view (c))) - return; - - if (c.count ("callback")) - { - string name (c.get ("callback")); - - // In case of the const instance, we only generate the call if - // there is a const callback. - // - string const& type (class_fq_name (c)); - - if (const_) - { - if (c.count ("callback-const")) - os << "static_cast< const " << type << "& > (x)." << - name << " (e, db);"; - } - else - os << "static_cast< " << type << "& > (x)." << name << " (e, db);"; - } - else if (obj) - inherits (c); - } - - protected: - bool const_; - traversal::inherits inherits_; - }; - - // // get/set null (composite value only) // @@ -211,7 +149,7 @@ namespace relational if (object (c)) traverse_object (c); - if (view (c)) + else if (view (c)) traverse_view (c); else if (composite (c)) traverse_composite (c); @@ -228,11 +166,11 @@ namespace relational traverse_object (type& c) { semantics::data_member* id (id_member (c)); - bool base_id (id ? &id->scope () != &c : false); // Comes from base. + bool base_id (id && &id->scope () != &c); // Comes from base. semantics::data_member* optimistic (context::optimistic (c)); - // Base class the contains the object id and version for optimistic + // Base class that contains the object id and version for optimistic // concurrency. // type* base ( @@ -246,47 +184,13 @@ namespace relational bool reuse_abst (abst && !poly); string const& type (class_fq_name (c)); - string traits ("access::object_traits< " + type + " >"); - string traits_impl ("access::object_traits_impl< " + type + - ", id_" + db.string () + " >"); + string traits ("access::object_traits_impl< " + type + ", id_" + + db.string () + " >"); os << "// " << class_name (c) << endl << "//" << endl << endl; - // id (object_type) - // - if (id != 0 || !reuse_abst) - { - os << "inline" << endl - << traits << "::id_type" << endl - << traits << "::" << endl - << "id (const object_type&" << (id != 0 ? " o" : "") << ")" - << "{"; - - if (id != 0) - { - if (base_id) - os << "return object_traits< " << class_fq_name (*base) << - " >::id (o);"; - else - { - // Get the id using the accessor expression. If this is not - // a synthesized expression, then output its location for - // easier error tracking. - // - member_access& ma (id->get ("get")); - - if (!ma.synthesized) - os << "// From " << location_string (ma.loc, true) << endl; - - os << "return " << ma.translate ("o") << ";"; - } - } - - os << "}"; - } - object_extra (c); if (id != 0 && base_id) @@ -298,8 +202,8 @@ namespace relational if (options.generate_query ()) { os << "inline" << endl - << traits_impl << "::id_type" << endl - << traits_impl << "::" << endl + << traits << "::id_type" << endl + << traits << "::" << endl << "id (const image_type& i)" << "{" << "return object_traits_impl< " << class_fq_name (*base) << @@ -312,8 +216,8 @@ namespace relational if (optimistic != 0) { os << "inline" << endl - << traits_impl << "::version_type" << endl - << traits_impl << "::" << endl + << traits << "::version_type" << endl + << traits << "::" << endl << "version (const image_type& i)" << "{" << "return object_traits_impl< " << class_fq_name (*base) << @@ -325,7 +229,7 @@ namespace relational // bind (id_image_type) // os << "inline" << endl - << "void " << traits_impl << "::" << endl + << "void " << traits << "::" << endl << "bind (" << bind_vector << " b, id_image_type& i" << (optimistic != 0 ? ", bool bv" : "") << ")" << "{" @@ -334,7 +238,7 @@ namespace relational << "}"; os << "inline" << endl - << "void " << traits_impl << "::" << endl + << "void " << traits << "::" << endl << "init (id_image_type& i, const id_type& id" << (optimistic != 0 ? ", const version_type* v" : "") << ")" << "{" @@ -350,7 +254,7 @@ namespace relational // check_version // os << "inline" << endl - << "bool " << traits_impl << "::" << endl + << "bool " << traits << "::" << endl << "check_version (const std::size_t* v, const image_type& i)" << "{" << "return "; @@ -370,7 +274,7 @@ namespace relational // update_version // os << "inline" << endl - << "void " << traits_impl << "::" << endl + << "void " << traits << "::" << endl << "update_version (std::size_t* v, const image_type& i, " << db << "::binding* b)" << "{"; @@ -394,7 +298,6 @@ namespace relational os << "}"; } - // // The rest does not apply to reuse-abstract objects. // if (reuse_abst) @@ -405,7 +308,7 @@ namespace relational if (id != 0 && !poly && optimistic == 0) { os << "inline" << endl - << "void " << traits_impl << "::" << endl + << "void " << traits << "::" << endl << "erase (database& db, const object_type& obj)" << "{" << "callback (db, obj, callback_event::pre_erase);" @@ -414,37 +317,12 @@ namespace relational << "}"; } - // callback () - // - os << "inline" << endl - << "void " << traits_impl << "::" << endl - << "callback (database& db, object_type& x, callback_event e)" - << endl - << "{" - << "ODB_POTENTIALLY_UNUSED (db);" - << "ODB_POTENTIALLY_UNUSED (x);" - << "ODB_POTENTIALLY_UNUSED (e);" - << endl; - callback_calls_->traverse (c, false); - os << "}"; - - os << "inline" << endl - << "void " << traits_impl << "::" << endl - << "callback (database& db, const object_type& x, callback_event e)" - << "{" - << "ODB_POTENTIALLY_UNUSED (db);" - << "ODB_POTENTIALLY_UNUSED (x);" - << "ODB_POTENTIALLY_UNUSED (e);" - << endl; - callback_calls_->traverse (c, true); - os << "}"; - // load_() // if (id != 0 && !(poly_derived || has_a (c, test_container))) { os << "inline" << endl - << "void " << traits_impl << "::" << endl + << "void " << traits << "::" << endl << "load_ ("; if (poly && !poly_derived) @@ -462,8 +340,8 @@ namespace relational // root_image () // os << "inline" << endl - << traits_impl << "::root_traits::image_type&" << endl - << traits_impl << "::" << endl + << traits << "::root_traits::image_type&" << endl + << traits << "::" << endl << "root_image (image_type& i)" << "{"; @@ -477,8 +355,8 @@ namespace relational // clone_image () // os << "inline" << endl - << traits_impl << "::image_type*" << endl - << traits_impl << "::" << endl + << traits << "::image_type*" << endl + << traits << "::" << endl << "clone_image (const image_type& i)" << "{"; @@ -496,7 +374,7 @@ namespace relational // copy_image () // os << "inline" << endl - << "void " << traits_impl << "::" << endl + << "void " << traits << "::" << endl << "copy_image (image_type& d, const image_type& s)" << "{"; @@ -513,7 +391,7 @@ namespace relational // free_image () // os << "inline" << endl - << "void " << traits_impl << "::" << endl + << "void " << traits << "::" << endl << "free_image (image_type* i)" << "{"; @@ -542,20 +420,6 @@ namespace relational << endl; view_extra (c); - - // callback () - // - os << "inline" << endl - << "void " << traits << "::" << endl - << "callback (database& db, view_type& x, callback_event e)" - << endl - << "{" - << "ODB_POTENTIALLY_UNUSED (db);" - << "ODB_POTENTIALLY_UNUSED (x);" - << "ODB_POTENTIALLY_UNUSED (e);" - << endl; - callback_calls_->traverse (c, false); - os << "}"; } virtual void @@ -604,8 +468,6 @@ namespace relational } private: - instance callback_calls_; - instance get_null_base_; traversal::inherits get_null_base_inherits_; instance get_null_member_; diff --git a/odb/relational/processor.cxx b/odb/relational/processor.cxx index f000858..5100739 100644 --- a/odb/relational/processor.cxx +++ b/odb/relational/processor.cxx @@ -305,497 +305,14 @@ namespace relational throw operation_failed (); } - process_access (m, "get"); - process_access (m, "set"); - process_index (m); - } - - // - // Process member access expressions. - // - - enum found_type - { - found_none, - found_some, // Found something but keep looking for a better one. - found_best - }; - - // Check if a function is a suitable accessor for this member. - // - found_type - check_accessor (semantics::data_member& m, - tree f, - string const& n, - member_access& ma, - bool strict) - { - // Must be const. - // - if (!DECL_CONST_MEMFUNC_P (f)) - return found_none; - - // Accessor is a function with no arguments (other than 'this'). - // - if (DECL_CHAIN (DECL_ARGUMENTS (f)) != NULL_TREE) - return found_none; - - // Note that to get the return type we have to use - // TREE_TYPE(TREE_TYPE()) and not DECL_RESULT, as - // suggested in the documentation. - // - tree r (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f)))); - int tc (TREE_CODE (r)); - - // In the strict mode make sure the function returns for non-array - // types a value or a (const) reference to the member type and for - // array types a (const) pointer to element type. In the lax mode - // we just check that the return value is not void. - // - if (strict) - { - semantics::type& t (utype (m)); - semantics::array* ar (dynamic_cast (&t)); - - if (ar != 0 && tc != POINTER_TYPE) - return found_none; - - tree bt (ar != 0 || tc == REFERENCE_TYPE ? TREE_TYPE (r) : r); - tree bt_mv (TYPE_MAIN_VARIANT (bt)); - - if ((ar != 0 ? ar->base_type () : t).tree_node () != bt_mv) - return found_none; - } - else if (r == void_type_node) - return found_none; - - cxx_tokens& e (ma.expr); - e.push_back (cxx_token (0, CPP_KEYWORD, "this")); - e.push_back (cxx_token (0, CPP_DOT)); - e.push_back (cxx_token (0, CPP_NAME, n)); - e.push_back (cxx_token (0, CPP_OPEN_PAREN, n)); - e.push_back (cxx_token (0, CPP_CLOSE_PAREN, n)); - - // See if it returns by value. - // - ma.by_value = (tc != REFERENCE_TYPE && tc != POINTER_TYPE); - - return found_best; - } - - // Check if a function is a suitable modifier for this member. - // - found_type - check_modifier (semantics::data_member& m, - tree f, - string const& n, - member_access& ma, - bool strict) - { - tree a (DECL_ARGUMENTS (f)); - a = DECL_CHAIN (a); // Skip this. - - // For a modifier, it can either be a function that returns a non- - // const reference (or non-const pointer, in case the member is an - // array) or a by-value modifier that sets a new value. If both are - // available, we prefer the former for efficiency. - // - cxx_tokens& e (ma.expr); - semantics::type& t (utype (m)); - semantics::array* ar (dynamic_cast (&t)); - - if (a == NULL_TREE) + if (m.count ("polymorphic-ref")) { - // Note that to get the return type we have to use - // TREE_TYPE(TREE_TYPE()) and not DECL_RESULT, as - // suggested in the documentation. - // - tree r (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f)))); - int tc (TREE_CODE (r)); - - // By-reference modifier. Should return a reference or a pointer. - // - if (tc != (ar != 0 ? POINTER_TYPE : REFERENCE_TYPE)) - return found_none; - - // The base type should not be const and, in strict mode, should - // match the member type. - // - tree bt (TREE_TYPE (r)); - - if (CP_TYPE_CONST_P (bt)) - return found_none; - - tree bt_mv (TYPE_MAIN_VARIANT (bt)); - - if (strict && (ar != 0 ? ar->base_type () : t).tree_node () != bt_mv) - return found_none; - - e.clear (); // Could contain by value modifier. - e.push_back (cxx_token (0, CPP_KEYWORD, "this")); - e.push_back (cxx_token (0, CPP_DOT)); - e.push_back (cxx_token (0, CPP_NAME, n)); - e.push_back (cxx_token (0, CPP_OPEN_PAREN, n)); - e.push_back (cxx_token (0, CPP_CLOSE_PAREN, n)); - - return found_best; + m.set ("not-null", true); + m.set ("deferred", false); + m.set ("on-delete", sema_rel::foreign_key::cascade); } - // Otherwise look for a by value modifier, which is a function - // with a single argument. - // - else if (DECL_CHAIN (a) == NULL_TREE) - { - // In the lax mode any function with a single argument works - // for us. And we don't care what it returns. - // - if (strict) - { - // In the strict mode make sure the argument matches the - // member. This is exactly the same logic as in accessor - // with regards to arrays, references, etc. - // - tree at (TREE_TYPE (a)); - int tc (TREE_CODE (at)); - - if (ar != 0 && tc != POINTER_TYPE) - return found_none; - - tree bt (ar != 0 || tc == REFERENCE_TYPE ? TREE_TYPE (at) : at); - tree bt_mv (TYPE_MAIN_VARIANT (bt)); - - if ((ar != 0 ? ar->base_type () : t).tree_node () != bt_mv) - return found_none; - } - - if (e.empty ()) - { - e.push_back (cxx_token (0, CPP_KEYWORD, "this")); - e.push_back (cxx_token (0, CPP_DOT)); - e.push_back (cxx_token (0, CPP_NAME, n)); - e.push_back (cxx_token (0, CPP_OPEN_PAREN, n)); - e.push_back (cxx_token (0, CPP_QUERY)); - e.push_back (cxx_token (0, CPP_CLOSE_PAREN, n)); - - // Continue searching in case there is version that returns a - // non-const reference which we prefer for efficiency. - // - return found_some; - } - else - return found_none; // We didn't find anything better. - } - - return found_none; - } - - void - process_access (semantics::data_member& m, std::string const& k) - { - bool virt (m.count ("virtual")); - - // Ignore certain special virtual members. - // - if (virt && (m.count ("polymorphic-ref") || m.count ("discriminator"))) - return; - - char const* kind (k == "get" ? "accessor" : "modifier"); - semantics::class_& c (dynamic_cast (m.scope ())); - - // If we don't have an access expression, try to come up with - // one. - // - if (!m.count (k)) - { - found_type found (found_none); - semantics::access const& a (m.named ().access ()); - member_access& ma (m.set (k, member_access (m.location (), true))); - - // If this member is not virtual and is either public or if we - // are a friend of this class, then go for the member directly. - // - if (!virt && (a == semantics::access::public_ || - c.get ("friend"))) - { - ma.expr.push_back (cxx_token (0, CPP_KEYWORD, "this")); - ma.expr.push_back (cxx_token (0, CPP_DOT)); - ma.expr.push_back (cxx_token (0, CPP_NAME, m.name ())); - found = found_best; - } - - // Otherwise, try to find a suitable accessor/modifier. - // - - // First try the original name. If that doesn't produce anything, - // then try the public name. - // - bool t (k == "get" - ? options.accessor_regex_trace () - : options.modifier_regex_trace ()); - regex_mapping const& re ( - k == "get" ? accessor_regex : modifier_regex); - - for (unsigned short j (0); found != found_best && j != 2; ++j) - { - string b (j == 0 ? m.name () : public_name (m, false)); - - // Skip the second pass if original and public names are the same. - // - if (j == 1 && b == m.name ()) - continue; - - if (t) - cerr << kind << (j == 0 ? " original" : " public") - << " name '" << b << "'" << endl; - - for (regex_mapping::const_iterator i (re.begin ()); - found != found_best && i != re.end (); - ++i) - { - if (t) - cerr << "try: '" << i->regex () << "' : "; - - if (!i->match (b)) - { - if (t) - cerr << '-' << endl; - continue; - } - - string n (i->replace (b)); - - if (t) - cerr << "'" << n << "' : "; - - tree decl ( - lookup_qualified_name ( - c.tree_node (), get_identifier (n.c_str ()), false, false)); - - if (decl == error_mark_node || TREE_CODE (decl) != BASELINK) - { - if (t) - cerr << '-' << endl; - continue; - } - - // OVL_* macros work for both FUNCTION_DECL and OVERLOAD. - // - for (tree o (BASELINK_FUNCTIONS (decl)); - o != 0; - o = OVL_NEXT (o)) - { - tree f (OVL_CURRENT (o)); - - // We are only interested in public non-static member - // functions. Note that TREE_PUBLIC() returns something - // other than what we need. - // - if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (f) || - TREE_PRIVATE (f) || TREE_PROTECTED (f)) - continue; - - found_type r (k == "get" - ? check_accessor (m, f, n, ma, true) - : check_modifier (m, f, n, ma, true)); - - if (r != found_none) - { - // Update the location of the access expression to point - // to this function. - // - ma.loc = location (DECL_SOURCE_LOCATION (f)); - found = r; - } - } - - if (t) - cerr << (found != found_none ? '+' : '-') << endl; - } - } - - // If that didn't work then the generated code won't be able - // to access this member. - // - if (found == found_none) - { - location const& l (m.location ()); - - if (virt) - { - error (l) << "no suitable " << kind << " function could be " - << "automatically found for virtual data member '" - << m.name () << "'" << endl; - - info (l) << "use '#pragma db " << k << "' to explicitly " - << "specify the " << kind << " function or " - << "expression" << endl; - } - else - { - error (l) << "data member '" << m.name () << "' is " - << a.string () << " and no suitable " << kind - << " function could be automatically found" << endl; - info (l) << "consider making class 'odb::access' a friend of " - << "class '" << class_name (c) << "'" << endl; - - info (l) << "or use '#pragma db " << k << "' to explicitly " - << "specify the " << kind << " function or " - << "expression" << endl; - } - - throw operation_failed (); - } - } - - member_access& ma (m.get (k)); - cxx_tokens& e (ma.expr); - - // If it is just a name, resolve it and convert to an appropriate - // expression. - // - if (e.size () == 1 && e.back ().type == CPP_NAME) - { - string n (e.back ().literal); - e.clear (); - - tree decl ( - lookup_qualified_name ( - c.tree_node (), get_identifier (n.c_str ()), false, false)); - - if (decl == error_mark_node) - { - error (ma.loc) << "unable to resolve data member or function " - << "name '" << n << "'" << endl; - throw operation_failed (); - } - - switch (TREE_CODE (decl)) - { - case FIELD_DECL: - { - e.push_back (cxx_token (0, CPP_KEYWORD, "this")); - e.push_back (cxx_token (0, CPP_DOT)); - e.push_back (cxx_token (0, CPP_NAME, n)); - break; - } - case BASELINK: - { - // OVL_* macros work for both FUNCTION_DECL and OVERLOAD. - // - for (tree o (BASELINK_FUNCTIONS (decl)); - o != 0; - o = OVL_NEXT (o)) - { - tree f (OVL_CURRENT (o)); - - // We are only interested in non-static member functions. - // - if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (f)) - continue; - - if ((k == "get" - ? check_accessor (m, f, n, ma, false) - : check_modifier (m, f, n, ma, false)) == found_best) - break; - } - - if (e.empty ()) - { - error (ma.loc) << "unable to find suitable " << kind - << " function '" << n << "'" << endl; - throw operation_failed (); - } - break; - } - default: - { - error (ma.loc) << "name '" << n << "' does not refer to a data " - << "member or function" << endl; - throw operation_failed (); - } - } - } - - // If there is no 'this' keyword, then add it as a prefix. - // - { - bool t (false); - for (cxx_tokens::iterator i (e.begin ()); i != e.end (); ++i) - { - if (i->type == CPP_KEYWORD && i->literal == "this") - { - t = true; - break; - } - } - - if (!t) - { - e.insert (e.begin (), cxx_token (0, CPP_DOT)); - e.insert (e.begin (), cxx_token (0, CPP_KEYWORD, "this")); - } - } - - // Check that there is no placeholder in the accessor expression. - // - if (k == "get" && ma.placeholder ()) - { - error (ma.loc) << "(?) placeholder in the accessor expression" - << endl; - throw operation_failed (); - } - - // Check that the member type is default-constructible if we - // have a by value modifier. - // - if (k == "set" && ma.placeholder ()) - { - semantics::class_* c (dynamic_cast (&utype (m))); - - // Assume all other types are default-constructible. - // - if (c != 0) - { - // If this type is a class template instantiation, then make - // sure it is instantiated. While types used in real members - // will be instantiated, this is not necessarily the case for - // virtual members. Without the instantiation we won't be able - // to detect whether the type has the default ctor. - // - // It would have been cleaner to do it in post_process_pragmas() - // but there we don't yet know whether we need the default ctor. - // And it is a good idea not to require instantiability unless - // we really need it. - // - tree type (c->tree_node ()); - - if (!COMPLETE_TYPE_P (type) && - CLASSTYPE_TEMPLATE_INSTANTIATION (type)) - { - // Reset input location so that we get nice diagnostics in - // case of an error. Use the location of the virtual pragma. - // - location_t loc (m.get ("virtual-location")); - input_location = loc; - - if (instantiate_class_template (type) == error_mark_node || - errorcount != 0 || - !COMPLETE_TYPE_P (type)) - { - error (loc) << "unable to instantiate virtual data member " << - "type" << endl; - throw operation_failed (); - } - } - - if (!c->default_ctor ()) - { - error (ma.loc) << "modifier expression requires member type " << - "to be default-constructible" << endl; - throw operation_failed (); - } - } - } + process_index (m); } // Convert index/unique specifiers to the index entry in the object. @@ -2351,44 +1868,7 @@ namespace relational struct class_: traversal::class_, context { class_ () - : std_string_ (0), std_string_hint_ (0), access_ (0) { - // Resolve the std::string type node. - // - using semantics::scope; - - for (scope::names_iterator_pair ip (unit.find ("std")); - ip.first != ip.second; ++ip.first) - { - if (scope* ns = dynamic_cast (&ip.first->named ())) - { - scope::names_iterator_pair jp (ns->find ("string")); - - if (jp.first != jp.second) - { - std_string_ = dynamic_cast ( - &jp.first->named ()); - std_string_hint_ = &*jp.first; - break; - } - } - } - - assert (std_string_ != 0); // No std::string? - - // Resolve odb::access, if any. - // - tree odb = lookup_qualified_name ( - global_namespace, get_identifier ("odb"), false, false); - - if (odb != error_mark_node) - { - access_ = lookup_qualified_name ( - odb, get_identifier ("access"), true, false); - - access_ = (access_ != error_mark_node ? TREE_TYPE (access_) : 0); - } - *this >> member_names_ >> member_; } @@ -2400,28 +1880,12 @@ namespace relational if (k == class_other) return; - // Check if odb::access is a friend of this class. - // - c.set ("friend", access_ != 0 && is_friend (c.tree_node (), access_)); - - // Assign pointer. - // - if (k == class_object || k == class_view) - assign_pointer (c); - - // Do some additional pre-processing. - // - if (k == class_object) - traverse_object_pre (c); - names (c); - // Do some additional post-processing. - // if (k == class_object) - traverse_object_post (c); + traverse_object (c); else if (k == class_view) - traverse_view_post (c); + traverse_view (c); } // @@ -2429,149 +1893,7 @@ namespace relational // virtual void - traverse_object_pre (type& c) - { - semantics::class_* poly_root (polymorphic (c)); - - // Determine whether it is a session object. - // - if (!c.count ("session")) - { - // If this is a derived class in a polymorphic hierarchy, - // then it should have the same session value as the root. - // - if (poly_root != 0 && poly_root != &c) - c.set ("session", session (*poly_root)); - else - { - // See if any of the namespaces containing this class specify - // the session value. - // - bool found (false); - for (semantics::scope* s (&c.scope ());; s = &s->scope_ ()) - { - using semantics::namespace_; - - namespace_* ns (dynamic_cast (s)); - - if (ns == 0) - continue; // Some other scope. - - if (ns->extension ()) - ns = &ns->original (); - - if (ns->count ("session")) - { - c.set ("session", ns->get ("session")); - found = true; - break; - } - - if (ns->global_scope ()) - break; - } - - // If still not found, then use the default value. - // - if (!found) - c.set ("session", options.generate_session ()); - } - } - - if (session (c)) - features.session_object = true; - - if (poly_root != 0) - { - using namespace semantics; - - semantics::data_member& idm (*id_member (*poly_root)); - - if (poly_root != &c) - { - // If we are a derived class in the polymorphic persistent - // class hierarchy, then add a synthesized virtual pointer - // member that points back to the root. - // - path const& f (idm.file ()); - size_t l (idm.line ()), col (idm.column ()); - - semantics::data_member& m ( - unit.new_node (f, l, col, tree (0))); - m.set ("virtual", true); - - // Make it the first member in the class. - // - node_position np (c, c.names_end ()); - unit.new_edge ( - np, m, idm.name (), access::public_); - - // Use the raw pointer as this member's type. - // - if (!poly_root->pointed_p ()) - { - // Create the pointer type in the graph. The pointer node - // in GCC seems to always be present, even if not explicitly - // used in the translation unit. - // - tree t (poly_root->tree_node ()); - tree ptr (TYPE_POINTER_TO (t)); - assert (ptr != 0); - ptr = TYPE_MAIN_VARIANT (ptr); - pointer& p (unit.new_node (f, l, col, ptr)); - unit.insert (ptr, p); - unit.new_edge (p, *poly_root); - assert (poly_root->pointed_p ()); - } - - unit.new_edge (m, poly_root->pointed ().pointer ()); - - m.set ("not-null", true); - m.set ("deferred", false); - m.set ("on-delete", sema_rel::foreign_key::cascade); - - // Mark it as a special kind of id. - // - m.set ("id", true); - m.set ("polymorphic-ref", true); - } - else - { - // If we are a root of the polymorphic persistent class hierarchy, - // then add a synthesized virtual member for the discriminator. - // Use the location of the polymorphic pragma as the location of - // this member. - // - location_t loc (c.get ("polymorphic-location")); - semantics::data_member& m ( - unit.new_node ( - path (LOCATION_FILE (loc)), - LOCATION_LINE (loc), - LOCATION_COLUMN (loc), - tree (0))); - m.set ("virtual", true); - - // Insert it after the id member (or first if this id comes - // from reuse-base). - // - node_position np ( - c, c.find (idm.named ())); - unit.new_edge ( - np, m, "typeid_", access::public_); - - belongs& edge (unit.new_edge (m, *std_string_)); - edge.hint (*std_string_hint_); - - m.set ("readonly", true); - m.set ("discriminator", true); - - c.set ("discriminator", &m); - } - } - } - - virtual void - traverse_object_post (type& c) + traverse_object (type& c) { // Process indexes. Here we need to do two things: resolve member // names to member paths and assign names to unnamed indexes. We @@ -2767,7 +2089,7 @@ namespace relational typedef vector relationships; virtual void - traverse_view_post (type& c) + traverse_view (type& c) { bool has_q (c.count ("query")); bool has_o (c.count ("objects")); @@ -3154,417 +2476,11 @@ namespace relational view_object& pointee_; }; - void - assign_pointer (type& c) - { - location_t loc (0); // Pragma location, or 0 if not used. - - try - { - string ptr; - string const& type (class_fq_name (c)); - - tree decl (0); // Resolved template node. - string decl_name; // User-provided template name. - tree resolve_scope (0); // Scope in which we resolve names. - - class_pointer const* cp (0); - bool cp_template (false); - - if (c.count ("pointer")) - { - cp = &c.get ("pointer"); - } - // If we are a derived type in polymorphic hierarchy, then use - // our root's pointer type by default. - // - else if (semantics::class_* r = polymorphic (c)) - { - if (&c != r && r->count ("pointer-template")) - cp = r->get ("pointer-template"); - } - - if (cp != 0) - { - string const& p (cp->name); - - if (p == "*") - { - ptr = type + "*"; - cp_template = true; - } - else if (p[p.size () - 1] == '*') - ptr = p; - else if (p.find ('<') != string::npos) - { - // Template-id. - // - ptr = p; - decl_name.assign (p, 0, p.find ('<')); - } - else - { - // This is not a template-id. Resolve it and see if it is a - // template or a type. - // - decl = resolve_name (p, cp->scope, true); - int tc (TREE_CODE (decl)); - - if (tc == TYPE_DECL) - { - ptr = p; - - // This can be a typedef'ed alias for a TR1 template-id. - // - if (tree ti = TYPE_TEMPLATE_INFO (TREE_TYPE (decl))) - { - decl = TI_TEMPLATE (ti); // DECL_TEMPLATE - - // Get to the most general template declaration. - // - while (DECL_TEMPLATE_INFO (decl)) - decl = DECL_TI_TEMPLATE (decl); - } - else - decl = 0; // Not a template. - } - else if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl)) - { - ptr = p + "< " + type + " >"; - decl_name = p; - cp_template = true; - } - else - { - error (cp->loc) - << "name '" << p << "' specified with db pragma pointer " - << "does not name a type or a template" << endl; - - throw operation_failed (); - } - } - - // Resolve scope is the scope of the pragma. - // - resolve_scope = cp->scope; - loc = cp->loc; - } - else - { - // See if any of the namespaces containing this class specify - // a pointer. - // - for (semantics::scope* s (&c.scope ());; s = &s->scope_ ()) - { - using semantics::namespace_; - - namespace_* ns (dynamic_cast (s)); - - if (ns == 0) - continue; // Some other scope. - - if (ns->extension ()) - ns = &ns->original (); - - if (!ns->count ("pointer")) - { - if (ns->global_scope ()) - break; - else - continue; - } - - cp = &ns->get ("pointer"); - string const& p (cp->name); - - // Namespace-specified pointer can only be '*' or are template. - // - if (p == "*") - ptr = type + "*"; - else if (p[p.size () - 1] == '*') - { - error (cp->loc) - << "name '" << p << "' specified with db pragma pointer " - << "at namespace level cannot be a raw pointer" << endl; - } - else if (p.find ('<') != string::npos) - { - error (cp->loc) - << "name '" << p << "' specified with db pragma pointer " - << "at namespace level cannot be a template-id" << endl; - } - else - { - // Resolve this name and make sure it is a template. - // - decl = resolve_name (p, cp->scope, true); - int tc (TREE_CODE (decl)); - - if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl)) - { - ptr = p + "< " + type + " >"; - decl_name = p; - } - else - { - error (cp->loc) - << "name '" << p << "' specified with db pragma pointer " - << "does not name a template" << endl; - } - } - - if (ptr.empty ()) - throw operation_failed (); - - cp_template = true; - - // Resolve scope is the scope of the pragma. - // - resolve_scope = cp->scope; - loc = cp->loc; - break; - } - - // Use the default pointer. - // - if (ptr.empty ()) - { - string const& p (options.default_pointer ()); - - if (p == "*") - ptr = type + "*"; - else - { - ptr = p + "< " + type + " >"; - decl_name = p; - } - - // Resolve scope is the scope of the class. - // - resolve_scope = c.scope ().tree_node (); - } - } - - // If this class is a root of a polymorphic hierarchy, then cache - // the pointer template so that we can use it for derived classes. - // - if (cp != 0 && cp_template && polymorphic (c) == &c) - c.set ("pointer-template", cp); - - // Check if we are using TR1. - // - if (decl != 0 || !decl_name.empty ()) - { - bool& tr1 (features.tr1_pointer); - bool& boost (features.boost_pointer); - - // First check the user-supplied name. - // - tr1 = tr1 - || decl_name.compare (0, 8, "std::tr1") == 0 - || decl_name.compare (0, 10, "::std::tr1") == 0; - - // If there was no match, also resolve the name since it can be - // a using-declaration for a TR1 template. - // - if (!tr1) - { - if (decl == 0) - decl = resolve_name (decl_name, resolve_scope, false); - - if (TREE_CODE (decl) != TEMPLATE_DECL || ! - DECL_CLASS_TEMPLATE_P (decl)) - { - // This is only checked for the --default-pointer option. - // - error (c.file (), c.line (), c.column ()) - << "name '" << decl_name << "' specified with the " - << "--default-pointer option does not name a class " - << "template" << endl; - - throw operation_failed (); - } - - string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER)); - - // In case of a boost TR1 implementation, we cannot distinguish - // between the boost:: and std::tr1:: usage since the latter is - // just a using-declaration for the former. - // - tr1 = tr1 - || n.compare (0, 8, "std::tr1") == 0 - || n.compare (0, 10, "::std::tr1") == 0; - - boost = boost - || n.compare (0, 17, "boost::shared_ptr") == 0 - || n.compare (0, 19, "::boost::shared_ptr") == 0; - } - } - - // Fully-qualify all the unqualified components of the name. - // - try - { - lex_.start (ptr); - ptr.clear (); - - string t; - bool punc (false); - bool scoped (false); - - for (cpp_ttype tt (lex_.next (t)); - tt != CPP_EOF; - tt = lex_.next (t)) - { - if (punc && tt > CPP_LAST_PUNCTUATOR) - ptr += ' '; - - punc = false; - - switch (static_cast (tt)) - { - case CPP_LESS: - { - ptr += "< "; - break; - } - case CPP_GREATER: - { - ptr += " >"; - break; - } - case CPP_COMMA: - { - ptr += ", "; - break; - } - case CPP_NAME: - { - // If the name was not preceeded with '::', look it - // up in the pragmas's scope and add the qualifer. - // - if (!scoped) - { - tree decl (resolve_name (t, resolve_scope, false)); - tree scope (CP_DECL_CONTEXT (decl)); - - if (scope != global_namespace) - { - ptr += "::"; - ptr += decl_as_string (scope, TFF_PLAIN_IDENTIFIER); - } - - ptr += "::"; - } - - ptr += t; - punc = true; - break; - } - case CPP_KEYWORD: - case CPP_NUMBER: - { - ptr += t; - punc = true; - break; - } - default: - { - ptr += t; - break; - } - } - - scoped = (tt == CPP_SCOPE); - } - } - catch (cxx_lexer::invalid_input const&) - { - throw operation_failed (); - } - - c.set ("object-pointer", ptr); - } - catch (invalid_name const& ex) - { - if (loc != 0) - error (loc) - << "name '" << ex.name () << "' specified with db pragma " - << "pointer is invalid" << endl; - else - error (c.file (), c.line (), c.column ()) - << "name '" << ex.name () << "' specified with the " - << "--default-pointer option is invalid" << endl; - - - throw operation_failed (); - } - catch (unable_to_resolve const& ex) - { - if (loc != 0) - error (loc) - << "unable to resolve name '" << ex.name () << "' specified " - << "with db pragma pointer" << endl; - else - error (c.file (), c.line (), c.column ()) - << "unable to resolve name '" << ex.name () << "' specified " - << "with the --default-pointer option" << endl; - - throw operation_failed (); - } - } - - private: - struct invalid_name - { - invalid_name (string const& n): name_ (n) {} - - string const& - name () const {return name_;} - - private: - string name_; - }; - - typedef lookup::unable_to_resolve unable_to_resolve; - - tree - resolve_name (string const& qn, tree scope, bool is_type) - { - try - { - string tl; - tree tn; - cpp_ttype tt, ptt; - - nlex_.start (qn); - tt = nlex_.next (tl, &tn); - - string name; - return lookup::resolve_scoped_name ( - nlex_, tt, tl, tn, ptt, scope, name, is_type); - } - catch (cxx_lexer::invalid_input const&) - { - throw invalid_name (qn); - } - catch (lookup::invalid_name const&) - { - throw invalid_name (qn); - } - } - private: cxx_string_lexer lex_; - cxx_string_lexer nlex_; // Nested lexer. data_member member_; traversal::names member_names_; - - semantics::type* std_string_; - semantics::names* std_string_hint_; - - tree access_; // odb::access node. }; } diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx index 4d56655..7516801 100644 --- a/odb/relational/source.cxx +++ b/odb/relational/source.cxx @@ -18,8 +18,8 @@ traverse_object (type& c) using semantics::data_member; data_member* id (id_member (c)); - bool auto_id (id ? id->count ("auto") : false); - bool base_id (id ? &id->scope () != &c : false); // Comes from base. + bool auto_id (id && auto_ (*id)); + bool base_id (id && &id->scope () != &c); // Comes from base. member_access* id_ma (id ? &id->get ("get") : 0); bool has_ptr (has_a (c, test_pointer)); diff --git a/odb/relational/validator.cxx b/odb/relational/validator.cxx index 1269406..9d14571 100644 --- a/odb/relational/validator.cxx +++ b/odb/relational/validator.cxx @@ -21,11 +21,228 @@ namespace relational // Pass 2. // + struct data_member2: traversal::data_member, context + { + data_member2 (bool& valid) + : valid_ (valid) + { + } + + virtual void + traverse (type& m) + { + if (transient (m)) + return; + + if (null (m)) + { + if (semantics::class_* c = composite_wrapper (utype (m))) + { + if (has_a (*c, test_container)) + { + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: composite member containing containers cannot " + << "be null" << endl; + + os << c->file () << ":" << c->line () << ":" << c->column () + << ": info: composite value type is defined here" << endl; + + valid_ = false; + } + } + } + } + + bool& valid_; + }; + + struct object_no_id_members: object_members_base + { + object_no_id_members (bool& valid) + : object_members_base (false, false, true), valid_ (valid), dm_ (0) + { + } + + virtual void + traverse_pointer (semantics::data_member& m, semantics::class_&) + { + if (inverse (m)) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: inverse object pointer member '" << member_prefix_ + << m.name () << "' in an object without an object id" << endl; + + valid_ = false; + } + } + + virtual void + traverse_container (semantics::data_member& m, semantics::type&) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: container member '" << member_prefix_ << m.name () + << "' in an object without an object id" << endl; + + valid_ = false; + } + + virtual void + traverse_composite (semantics::data_member* m, semantics::class_& c) + { + semantics::data_member* old_dm (dm_); + + if (dm_ == 0) + dm_ = m; + + object_members_base::traverse_composite (m, c); + + dm_ = old_dm; + } + + private: + bool& valid_; + semantics::data_member* dm_; // Direct object data member. + }; + + struct composite_id_members: object_members_base + { + composite_id_members (bool& valid) + : object_members_base (false, false, true), valid_ (valid), dm_ (0) + { + } + + virtual void + traverse_pointer (semantics::data_member& m, semantics::class_&) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: object pointer member '" << member_prefix_ << m.name () + << "' in a composite value type that is used as an object id" + << endl; + + valid_ = false; + } + + virtual void + traverse_simple (semantics::data_member& m) + { + if (readonly (member_path_, member_scope_)) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: readonly member '" << member_prefix_ << m.name () + << "' in a composite value type that is used as an object id" + << endl; + + valid_ = false; + } + } + + virtual void + traverse_container (semantics::data_member& m, semantics::type&) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: container member '" << member_prefix_ << m.name () + << "' in a composite value type that is used as an object id" + << endl; + + valid_ = false; + } + + virtual void + traverse_composite (semantics::data_member* m, semantics::class_& c) + { + semantics::data_member* old_dm (dm_); + + if (dm_ == 0) + dm_ = m; + + object_members_base::traverse_composite (m, c); + + dm_ = old_dm; + } + + private: + bool& valid_; + semantics::data_member* dm_; // Direct composite member. + }; + + struct view_members: object_members_base + { + view_members (bool& valid) + : object_members_base (false, false, true), valid_ (valid), dm_ (0) + { + } + + virtual void + traverse_simple (semantics::data_member& m) + { + if (object_pointer (utype (m))) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: view data member '" << member_prefix_ << m.name () + << "' is an object pointer" << endl; + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << ": info: views cannot contain object pointers" << endl; + + valid_ = false; + } + } + + virtual void + traverse_container (semantics::data_member& m, semantics::type&) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: view data member '" << member_prefix_ << m.name () + << "' is a container" << endl; + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << ": info: views cannot contain containers" << endl; + + valid_ = false; + } + + virtual void + traverse_composite (semantics::data_member* m, semantics::class_& c) + { + semantics::data_member* old_dm (dm_); + + if (dm_ == 0) + dm_ = m; + + object_members_base::traverse_composite (m, c); + + dm_ = old_dm; + } + + private: + bool& valid_; + semantics::data_member* dm_; // Direct view data member. + }; + struct class2: traversal::class_, context { class2 (bool& valid) - : valid_ (valid) + : valid_ (valid), + data_member_ (valid), + object_no_id_members_ (valid), + composite_id_members_ (valid), + view_members_ (valid) { + *this >> data_member_names_ >> data_member_; } virtual void @@ -56,6 +273,66 @@ namespace relational virtual void traverse_object (type& c) { + semantics::data_member* id (id_member (c)); + + if (id != 0) + { + if (semantics::class_* cm = composite_wrapper (utype (*id))) + { + // Composite id cannot be auto. + // + if (auto_ (*id)) + { + os << id->file () << ":" << id->line () << ":" << id->column () + << ": error: composite id cannot be automatically assigned" + << endl; + + valid_ = false; + } + + // Make sure we don't have any containers or pointers in this + // composite value type. + // + if (valid_) + { + composite_id_members_.traverse (*cm); + + if (!valid_) + os << id->file () << ":" << id->line () << ":" << id->column () + << ": info: composite id is defined here" << endl; + } + + // Check that the composite value type is default-constructible. + // + if (!cm->default_ctor ()) + { + os << cm->file () << ":" << cm->line () << ":" << cm->column () + << ": error: composite value type that is used as object id " + << "is not default-constructible" << endl; + + os << cm->file () << ":" << cm->line () << ":" << cm->column () + << ": info: provide default constructor for this value type" + << endl; + + os << id->file () << ":" << id->line () << ":" << id->column () + << ": info: composite id is defined here" << endl; + + valid_ = false; + } + } + } + else + { + if (!abstract (c)) + { + // Make sure we don't have any containers or inverse pointers. + // + object_no_id_members_.traverse (c); + } + } + + names (c); + // Validate indexes. // { @@ -90,17 +367,30 @@ namespace relational } virtual void - traverse_view (type&) + traverse_view (type& c) { + // Make sure we don't have any containers or object pointers. + // + view_members_.traverse (c); + + names (c); } virtual void - traverse_composite (type&) + traverse_composite (type& c) { + names (c); } public: bool& valid_; + + data_member2 data_member_; + traversal::names data_member_names_; + + object_no_id_members object_no_id_members_; + composite_id_members composite_id_members_; + view_members view_members_; }; } diff --git a/odb/validator.cxx b/odb/validator.cxx index aba82a5..d0b9ee2 100644 --- a/odb/validator.cxx +++ b/odb/validator.cxx @@ -821,233 +821,13 @@ namespace // Pass 2. // - struct data_member2: traversal::data_member, context - { - data_member2 (bool& valid) - : valid_ (valid) - { - } - - virtual void - traverse (type& m) - { - if (transient (m)) - return; - - if (null (m)) - { - if (semantics::class_* c = composite_wrapper (utype (m))) - { - if (has_a (*c, test_container)) - { - os << m.file () << ":" << m.line () << ":" << m.column () << ":" - << " error: composite member containing containers cannot " - << "be null" << endl; - - os << c->file () << ":" << c->line () << ":" << c->column () << ":" - << " info: composite value type is defined here" << endl; - - valid_ = false; - } - } - } - } - - bool& valid_; - }; - - struct object_no_id_members: object_members_base - { - object_no_id_members (bool& valid) - : object_members_base (false, false, true), valid_ (valid), dm_ (0) - { - } - - virtual void - traverse_pointer (semantics::data_member& m, semantics::class_&) - { - if (inverse (m)) - { - semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: inverse object pointer member '" << member_prefix_ - << m.name () << "' in an object without an object id" << endl; - - valid_ = false; - } - } - - virtual void - traverse_container (semantics::data_member& m, semantics::type&) - { - semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: container member '" << member_prefix_ << m.name () - << "' in an object without an object id" << endl; - - valid_ = false; - } - - virtual void - traverse_composite (semantics::data_member* m, semantics::class_& c) - { - semantics::data_member* old_dm (dm_); - - if (dm_ == 0) - dm_ = m; - - object_members_base::traverse_composite (m, c); - - dm_ = old_dm; - } - - private: - bool& valid_; - semantics::data_member* dm_; // Direct object data member. - }; - - struct composite_id_members: object_members_base - { - composite_id_members (bool& valid) - : object_members_base (false, false, true), valid_ (valid), dm_ (0) - { - } - - virtual void - traverse_pointer (semantics::data_member& m, semantics::class_&) - { - semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: object pointer member '" << member_prefix_ << m.name () - << "' in a composite value type that is used as an object id" << endl; - - valid_ = false; - } - - virtual void - traverse_simple (semantics::data_member& m) - { - if (readonly (member_path_, member_scope_)) - { - semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: readonly member '" << member_prefix_ << m.name () - << "' in a composite value type that is used as an object id" - << endl; - - valid_ = false; - } - } - - virtual void - traverse_container (semantics::data_member& m, semantics::type&) - { - semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: container member '" << member_prefix_ << m.name () - << "' in a composite value type that is used as an object id" << endl; - - valid_ = false; - } - - virtual void - traverse_composite (semantics::data_member* m, semantics::class_& c) - { - semantics::data_member* old_dm (dm_); - - if (dm_ == 0) - dm_ = m; - - object_members_base::traverse_composite (m, c); - - dm_ = old_dm; - } - - private: - bool& valid_; - semantics::data_member* dm_; // Direct composite member. - }; - - struct view_members: object_members_base - { - view_members (bool& valid) - : object_members_base (false, false, true), valid_ (valid), dm_ (0) - { - } - - virtual void - traverse_simple (semantics::data_member& m) - { - if (object_pointer (utype (m))) - { - semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: view data member '" << member_prefix_ << m.name () - << "' is an object pointer" << endl; - - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << ": info: views cannot contain object pointers" << endl; - - valid_ = false; - } - } - - virtual void - traverse_container (semantics::data_member& m, semantics::type&) - { - semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: view data member '" << member_prefix_ << m.name () - << "' is a container" << endl; - - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << ": info: views cannot contain containers" << endl; - - valid_ = false; - } - - virtual void - traverse_composite (semantics::data_member* m, semantics::class_& c) - { - semantics::data_member* old_dm (dm_); - - if (dm_ == 0) - dm_ = m; - - object_members_base::traverse_composite (m, c); - - dm_ = old_dm; - } - - private: - bool& valid_; - semantics::data_member* dm_; // Direct view data member. - }; - - // - // struct class2: traversal::class_, context { class2 (bool& valid) - : valid_ (valid), - data_member_ (valid), - object_no_id_members_ (valid), - composite_id_members_ (valid), - view_members_ (valid) + : valid_ (valid), has_lt_operator_ (0) { - *this >> data_member_names_ >> data_member_; - - // Find the has_lt_operator function template.. + // Find the has_lt_operator function template. // - has_lt_operator_ = 0; - tree odb ( lookup_qualified_name ( global_namespace, get_identifier ("odb"), false, false)); @@ -1098,148 +878,79 @@ namespace virtual void traverse_object (type& c) { - semantics::data_member* id (id_member (c)); - - if (id != 0) + if (semantics::data_member* id = id_member (c)) { - if (semantics::class_* cm = composite_wrapper (utype (*id))) - { - // Composite id cannot be auto. - // - if (auto_ (*id)) - { - os << id->file () << ":" << id->line () << ":" << id->column () - << ": error: composite id cannot be automatically assigned" - << endl; + semantics::type& t (utype (*id)); - valid_ = false; - } + // If this is a session object, make sure that the id type can + // be compared. + // + if (session (c) && has_lt_operator_ != 0) + { + tree args (make_tree_vec (1)); + TREE_VEC_ELT (args, 0) = t.tree_node (); - // Make sure we don't have any containers or pointers in this - // composite value type. - // - if (valid_) - { - composite_id_members_.traverse (*cm); + tree inst ( + instantiate_template ( + has_lt_operator_, args, tf_none)); - if (!valid_) - os << id->file () << ":" << id->line () << ":" << id->column () - << ": info: composite id is defined here" << endl; - } + bool v (inst != error_mark_node); - // Check that the composite value type is default-constructible. - // - if (!cm->default_ctor ()) + if (v && + DECL_TEMPLATE_INSTANTIATION (inst) && + !DECL_TEMPLATE_INSTANTIATED (inst)) { - os << cm->file () << ":" << cm->line () << ":" << cm->column () - << ": error: composite value type that is used as object id " - << "is not default-constructible" << endl; - - os << cm->file () << ":" << cm->line () << ":" << cm->column () - << ": info: provide default constructor for this value type" - << endl; + // Instantiate this function template to see if the value type + // provides operator<. Unfortunately, GCC instantiate_decl() + // does not provide any control over the diagnostics it issues + // in case of an error. To work around this, we are going to + // temporarily redirect diagnostics to /dev/null, which is + // where asm_out_file points to (see plugin.cxx). + // + int ec (errorcount); + FILE* s (global_dc->printer->buffer->stream); + global_dc->printer->buffer->stream = asm_out_file; - os << id->file () << ":" << id->line () << ":" << id->column () - << ": info: composite id is defined here" << endl; + instantiate_decl (inst, false, false); - valid_ = false; + global_dc->printer->buffer->stream = s; + v = (ec == errorcount); } - // If this is a session object, make sure that the composite - // value can be compared. - // - if (session (c) && has_lt_operator_ != 0) + if (!v) { - tree args (make_tree_vec (1)); - TREE_VEC_ELT (args, 0) = cm->tree_node (); + os << t.file () << ":" << t.line () << ":" << t.column () + << ": error: value type that is used as object id in " + << "persistent class with session support does not define " + << "the less than (<) comparison" << endl; - tree inst ( - instantiate_template ( - has_lt_operator_, args, tf_none)); + os << t.file () << ":" << t.line () << ":" << t.column () + << ": info: provide operator< for this value type" << endl; - bool v (inst != error_mark_node); - - if (v && - DECL_TEMPLATE_INSTANTIATION (inst) && - !DECL_TEMPLATE_INSTANTIATED (inst)) - { - // Instantiate this function template to see if the value type - // provides operator<. Unfortunately, GCC instantiate_decl() - // does not provide any control over the diagnostics it issues - // in case of an error. To work around this, we are going to - // temporarily redirect diagnostics to /dev/null, which is - // where asm_out_file points to (see plugin.cxx). - // - int ec (errorcount); - FILE* s (global_dc->printer->buffer->stream); - global_dc->printer->buffer->stream = asm_out_file; - - instantiate_decl (inst, false, false); - - global_dc->printer->buffer->stream = s; - v = (ec == errorcount); - } - - if (!v) - { - os << cm->file () << ":" << cm->line () << ":" << cm->column () - << ": error: composite value type that is used as object id " - << "in persistent class with session support does not " - << "define the less than (<) comparison" - << endl; - - os << cm->file () << ":" << cm->line () << ":" << cm->column () - << ": info: provide operator< for this value type" << endl; - - os << id->file () << ":" << id->line () << ":" << id->column () - << ": info: composite id is defined here" << endl; + os << id->file () << ":" << id->line () << ":" << id->column () + << ": info: id member is defined here" << endl; - os << c.file () << ":" << c.line () << ":" << c.column () - << ": info: persistent class is defined here" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () + << ": info: persistent class is defined here" << endl; - valid_ = false; - } + valid_ = false; } } } - else - { - if (!abstract (c)) - { - // Make sure we don't have any containers or inverse pointers. - // - object_no_id_members_.traverse (c); - } - } - - names (c); } virtual void - traverse_view (type& c) + traverse_view (type&) { - // Make sure we don't have any containers or object pointers. - // - view_members_.traverse (c); - - names (c); } virtual void - traverse_composite (type& c) + traverse_composite (type&) { - names (c); } bool& valid_; tree has_lt_operator_; - - data_member2 data_member_; - traversal::names data_member_names_; - - object_no_id_members object_no_id_members_; - composite_id_members composite_id_members_; - view_members view_members_; }; } @@ -1251,6 +962,7 @@ validate (options const& ops, unsigned short pass) { bool valid (true); + database db (ops.database ()[0]); // Validate options. // @@ -1263,6 +975,25 @@ validate (options const& ops, valid = false; } + // Multi-database support options. + // + if (ops.multi_database () == multi_database::dynamic && + ops.default_database_specified () && + ops.default_database () != database::common) + { + cerr << "error: when dynamic multi-database support is used, the " << + "default database can only be 'common'" << endl; + valid = false; + } + + if (db == database::common && + ops.multi_database () == multi_database::disabled) + { + cerr << "error: 'common' database is only valid with multi-database " << + "support enabled" << endl; + valid = false; + } + if (!valid) throw failed (); @@ -1319,13 +1050,29 @@ validate (options const& ops, if (!valid) throw failed (); - try + switch (db) { - relational::validator v; - v.validate (ops, f, u, p, pass); - } - catch (relational::validator::failed const&) - { - throw failed (); + case database::common: + { + break; + } + case database::mssql: + case database::mysql: + case database::oracle: + case database::pgsql: + case database::sqlite: + { + try + { + relational::validator v; + v.validate (ops, f, u, p, pass); + } + catch (relational::validator::failed const&) + { + throw failed (); + } + + break; + } } } -- cgit v1.1