From 8d89bf37dd4ef3cb7373e1841ff57a53916fff0d Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 21 Mar 2012 08:36:21 +0200 Subject: Polymorphic inheritance support --- odb/common.cxx | 37 +- odb/common.hxx | 37 +- odb/context.cxx | 75 +- odb/context.hxx | 62 +- odb/features.hxx | 24 + odb/generator.cxx | 48 +- odb/generator.hxx | 6 +- odb/header.cxx | 23 +- odb/plugin.cxx | 11 +- odb/pragma.cxx | 15 +- odb/processor.cxx | 7 +- odb/processor.hxx | 6 +- odb/relational/common.cxx | 178 +- odb/relational/common.hxx | 30 + odb/relational/context.cxx | 1 + odb/relational/context.hxx | 1 + odb/relational/header.cxx | 805 +++++++- odb/relational/header.hxx | 732 +++----- odb/relational/inline.cxx | 3 + odb/relational/inline.hxx | 259 ++- odb/relational/model.cxx | 2 +- odb/relational/model.hxx | 18 +- odb/relational/mssql/context.cxx | 4 +- odb/relational/mssql/context.hxx | 1 + odb/relational/mssql/header.cxx | 33 +- odb/relational/mssql/source.cxx | 168 +- odb/relational/mysql/context.cxx | 4 +- odb/relational/mysql/context.hxx | 1 + odb/relational/mysql/source.cxx | 134 +- odb/relational/oracle/context.cxx | 4 +- odb/relational/oracle/context.hxx | 1 + odb/relational/oracle/header.cxx | 33 +- odb/relational/oracle/source.cxx | 154 +- odb/relational/pgsql/context.cxx | 4 +- odb/relational/pgsql/context.hxx | 1 + odb/relational/pgsql/header.cxx | 21 +- odb/relational/pgsql/source.cxx | 238 +-- odb/relational/processor.cxx | 426 ++++- odb/relational/schema-source.hxx | 10 +- odb/relational/source.cxx | 3245 +++++++++++++++++++++++++++++++++ odb/relational/source.hxx | 2634 ++++++-------------------- odb/relational/sqlite/context.cxx | 4 +- odb/relational/sqlite/context.hxx | 1 + odb/relational/sqlite/source.cxx | 124 +- odb/semantics/class.hxx | 2 +- odb/semantics/elements.cxx | 29 +- odb/semantics/elements.hxx | 71 + odb/semantics/relational/elements.cxx | 6 +- odb/semantics/relational/elements.hxx | 3 +- odb/semantics/unit.hxx | 2 + odb/traversal/elements.cxx | 20 + odb/traversal/elements.hxx | 14 + odb/validator.cxx | 130 +- odb/validator.hxx | 2 + 54 files changed, 6265 insertions(+), 3639 deletions(-) create mode 100644 odb/features.hxx diff --git a/odb/common.cxx b/odb/common.cxx index 35bfc87..28b80cb 100644 --- a/odb/common.cxx +++ b/odb/common.cxx @@ -85,12 +85,23 @@ traverse (semantics::class_& c) return; } - semantics::class_* prev (context::cur_object); - context::cur_object = &c; + if (top_level_) + top_level_ = false; + else + { + // Unless requested otherwise, don't go into bases if we are a derived + // type in a polymorphic hierarchy. + // + if (!traverse_poly_base_ && polymorphic (c)) + return; + } if (context::top_object == 0) context::top_object = &c; + semantics::class_* prev (context::cur_object); + context::cur_object = &c; + if (member_scope_.empty ()) member_scope_.push_back (class_inheritance_chain ()); @@ -138,10 +149,10 @@ traverse (semantics::class_& c) if (member_scope_.back ().empty ()) member_scope_.pop_back (); + context::cur_object = prev; + if (prev == 0) context::top_object = 0; - - context::cur_object = prev; } void object_members_base:: @@ -363,15 +374,23 @@ traverse (semantics::class_& c) if (top_level_) top_level_ = false; + else + { + // Unless requested otherwise, don't go into bases if we are a derived + // type in a polymorphic hierarchy. + // + if (!traverse_poly_base_ && polymorphic (c)) + return; + } semantics::class_* prev (0); if (k == class_object || k == class_view) { - prev = context::cur_object; - context::cur_object = &c; - if (context::top_object == 0) context::top_object = &c; + + prev = context::cur_object; + context::cur_object = &c; } if (member_scope_.empty ()) @@ -393,10 +412,10 @@ traverse (semantics::class_& c) if (k == class_object || k == class_view) { + context::cur_object = prev; + if (prev == 0) context::top_object = 0; - - context::cur_object = prev; } if (f && !first_) diff --git a/odb/common.hxx b/odb/common.hxx index ffbd2d6..bc45d00 100644 --- a/odb/common.hxx +++ b/odb/common.hxx @@ -67,25 +67,33 @@ struct object_members_base: traversal::class_, virtual context traverse_view (semantics::class_&); public: - object_members_base () - : member_ (*this) + object_members_base (bool traverse_poly_base = false) + : top_level_ (true), member_ (*this) { - init (false, false, false); + init (false, false, false, traverse_poly_base); } object_members_base (bool build_flat_prefix, bool build_table_prefix, - bool build_member_prefix) - : member_ (*this) + bool build_member_prefix, + bool traverse_poly_base = false) + : top_level_ (true), member_ (*this) { - init (build_flat_prefix, build_table_prefix, build_member_prefix); + init (build_flat_prefix, + build_table_prefix, + build_member_prefix, + traverse_poly_base); } object_members_base (object_members_base const& x) : context (), //@@ -Wextra + top_level_ (true), member_ (*this) { - init (x.build_flat_prefix_, x.build_table_prefix_, x.build_member_prefix_); + init (x.build_flat_prefix_, + x.build_table_prefix_, + x.build_member_prefix_, + x.traverse_poly_base_); } virtual void @@ -117,11 +125,13 @@ private: void init (bool build_flat_prefix, bool build_table_prefix, - bool build_member_prefix) + bool build_member_prefix, + bool traverse_poly_base) { build_flat_prefix_ = build_flat_prefix; build_table_prefix_ = build_table_prefix; build_member_prefix_ = build_member_prefix; + traverse_poly_base_ = traverse_poly_base; *this >> names_ >> member_; *this >> inherits_ >> *this; @@ -149,6 +159,10 @@ private: bool build_table_prefix_; bool build_member_prefix_; + bool traverse_poly_base_; + + bool top_level_; + member member_; traversal::names names_; traversal::inherits inherits_; @@ -204,9 +218,11 @@ struct object_columns_base: traversal::class_, virtual context public: object_columns_base (bool first = true, - string const& column_prefix = string ()) + string const& column_prefix = string (), + bool traverse_poly_base = false) : column_prefix_ (column_prefix), root_ (0), + traverse_poly_base_ (traverse_poly_base), first_ (first), top_level_ (true), member_ (*this) @@ -218,6 +234,7 @@ public: : context (), //@@ -Wextra column_prefix_ (x.column_prefix_), root_ (0), + traverse_poly_base_ (x.traverse_poly_base_), first_ (x.first_), top_level_ (true), member_ (*this) @@ -321,6 +338,8 @@ private: object_columns_base& oc_; }; + bool traverse_poly_base_; + bool first_; bool top_level_; diff --git a/odb/context.cxx b/odb/context.cxx index 265d4ca..f699f38 100644 --- a/odb/context.cxx +++ b/odb/context.cxx @@ -122,6 +122,7 @@ auto_ptr create_context (ostream& os, semantics::unit& unit, options const& ops, + features& f, semantics::relational::model* m) { auto_ptr r; @@ -130,27 +131,27 @@ create_context (ostream& os, { case database::mssql: { - r.reset (new relational::mssql::context (os, unit, ops, m)); + r.reset (new relational::mssql::context (os, unit, ops, f, m)); break; } case database::mysql: { - r.reset (new relational::mysql::context (os, unit, ops, m)); + r.reset (new relational::mysql::context (os, unit, ops, f, m)); break; } case database::oracle: { - r.reset (new relational::oracle::context (os, unit, ops, m)); + r.reset (new relational::oracle::context (os, unit, ops, f, m)); break; } case database::pgsql: { - r.reset (new relational::pgsql::context (os, unit, ops, m)); + r.reset (new relational::pgsql::context (os, unit, ops, f, m)); break; } case database::sqlite: { - r.reset (new relational::sqlite::context (os, unit, ops, m)); + r.reset (new relational::sqlite::context (os, unit, ops, f, m)); break; } } @@ -169,11 +170,13 @@ context:: context (ostream& os_, semantics::unit& u, options_type const& ops, + features_type& f, data_ptr d) : data_ (d ? d : data_ptr (new (shared) data (os_))), os (data_->os_), unit (u), options (ops), + features (f), db (options.database ()), keyword_set (data_->keyword_set_), include_regex (data_->include_regex_), @@ -201,6 +204,7 @@ context () os (current ().os), unit (current ().unit), options (current ().options), + features (current ().features), db (current ().db), keyword_set (current ().keyword_set), include_regex (current ().include_regex), @@ -428,6 +432,28 @@ null (semantics::data_member& m, string const& kp) } } +size_t context:: +polymorphic_depth (semantics::class_& c) +{ + if (c.count ("polymorphic-depth")) + return c.get ("polymorphic-depth"); + + // Calculate our hierarchy depth (number of classes). + // + using semantics::class_; + + class_* root (polymorphic (c)); + assert (root != 0); + + size_t r (1); // One for the root. + + for (class_* b (&c); b != root; b = &polymorphic_base (*b)) + ++r; + + c.set ("polymorphic-depth", r); + return r; +} + context::class_kind_type context:: class_kind (semantics::class_& c) { @@ -1180,6 +1206,28 @@ escape (string const& name) const return r; } +string context:: +make_guard (string const& s) const +{ + // Split words, e.g., "FooBar" to "Foo_Bar" and convert everything + // to upper case. + // + string r; + for (string::size_type i (0), n (s.size ()); i < n - 1; ++i) + { + char c1 (s[i]); + char c2 (s[i + 1]); + + r += toupper (c1); + + if (isalpha (c1) && isalpha (c2) && islower (c1) && isupper (c2)) + r += "_"; + } + r += toupper (s[s.size () - 1]); + + return escape (r); +} + static string charlit (unsigned int u) { @@ -1351,6 +1399,11 @@ namespace c_.readonly++; else if (context::version (m)) c_.optimistic_managed++; + + // For now discriminator can only be a simple value. + // + if (discriminator (m)) + c_.discriminator++; } context::column_count_type c_; @@ -1375,7 +1428,9 @@ namespace struct has_a_impl: object_members_base { has_a_impl (unsigned short flags) - : r_ (0), flags_ (flags) + : object_members_base ((flags & context::include_base) != 0), + r_ (0), + flags_ (flags) { } @@ -1386,8 +1441,14 @@ namespace } virtual void - traverse_pointer (semantics::data_member&, semantics::class_&) + traverse_pointer (semantics::data_member& m, semantics::class_&) { + // Ignore polymorphic id references; they are represented as + // pointers but are normally handled in a special way. + // + if (m.count ("polymorphic-ref")) + return; + if (context::is_a (member_path_, member_scope_, flags_)) r_++; diff --git a/odb/context.hxx b/odb/context.hxx index b590ea4..cdb49ac 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -442,6 +443,42 @@ public: return m.count ("version"); } + // Polymorphic inheritance. Return root of the hierarchy or NULL if + // not polymorphic. + // + static semantics::class_* + polymorphic (semantics::class_& c) + { + // Set by the validator. + // + return c.get ("polymorphic-root", 0); + } + + static semantics::class_& + polymorphic_base (semantics::class_& c) + { + // Set by the validator. + // + return *c.get ("polymorphic-base"); + } + + static size_t + polymorphic_depth (semantics::class_&); + + static bool + discriminator (semantics::data_member& m) + { + return m.count ("discriminator"); + } + + static semantics::data_member* + discriminator (semantics::class_& c) + { + // Set by type processor. + // + return c.get ("discriminator", 0); + } + // // typedef ::class_kind class_kind_type; @@ -556,6 +593,12 @@ public: string escape (string const&) const; + // Make C++ include guard name by split words, e.g., "FooBar" to + // "Foo_Bar" and converting everything to upper case. + // + string + make_guard (string const&) const; + // Return a string literal that can be used in C++ source code. It // includes "". // @@ -572,7 +615,8 @@ public: id (0), inverse (0), readonly (0), - optimistic_managed (0) + optimistic_managed (0), + discriminator (0) { } @@ -581,6 +625,7 @@ public: size_t inverse; size_t readonly; size_t optimistic_managed; + size_t discriminator; }; static column_count_type @@ -589,7 +634,8 @@ public: static semantics::data_member* id_member (semantics::class_& c) { - // Set by the validator. May not be there for abstract objects. + // Set by the validator. May not be there for reuse-abstract + // classes or classes without object id. // return c.get ("id-member", 0); } @@ -695,7 +741,12 @@ public: static unsigned short const test_inverse_container = 0x20; static unsigned short const test_readonly_container = 0x40; - static unsigned short const exclude_base = 0x8000; + // By default the test goes into bases for non-polymorphic + // hierarchies and doesn't go for polymorphic. The following + // flags can be used to alter this behavior. + // + static unsigned short const exclude_base = 0x4000; + static unsigned short const include_base = 0x8000; bool is_a (data_member_path const& mp, @@ -808,9 +859,12 @@ protected: data_ptr data_; public: + typedef ::features features_type; + std::ostream& os; semantics::unit& unit; options_type const& options; + features_type& features; database const db; keyword_set_type const& keyword_set; @@ -855,6 +909,7 @@ public: context (std::ostream&, semantics::unit&, options_type const&, + features_type&, data_ptr = data_ptr ()); static context& @@ -877,6 +932,7 @@ std::auto_ptr create_context (std::ostream&, semantics::unit&, options const&, + features&, semantics::relational::model*); // Checks if scope Y names any of X. diff --git a/odb/features.hxx b/odb/features.hxx new file mode 100644 index 0000000..0468acb --- /dev/null +++ b/odb/features.hxx @@ -0,0 +1,24 @@ +// file : odb/features.hxx +// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_FEATURES_HXX +#define ODB_FEATURES_HXX + +#include // std::memset + +// Optional features used by client code that affect generated code. +// +struct features +{ + features () {std::memset (this, 0, sizeof (features));} + + bool tr1_pointer; + bool boost_pointer; + bool simple_object; + bool polymorphic_object; + bool no_id_object; + bool view; +}; + +#endif // ODB_FEATURES_HXX diff --git a/odb/generator.cxx b/odb/generator.cxx index c63b52f..ffed9d9 100644 --- a/odb/generator.cxx +++ b/odb/generator.cxx @@ -33,30 +33,6 @@ namespace "// compiler for C++.\n" "//\n\n"; - string - make_guard (string const& file, context& ctx) - { - string g (file); - - // Split words, e.g., "FooBar" to "Foo_Bar" and convert everything - // to upper case. - // - string r; - for (string::size_type i (0), n (g.size ()); i < n - 1; ++i) - { - char c1 (g[i]); - char c2 (g[i + 1]); - - r += toupper (c1); - - if (isalpha (c1) && isalpha (c2) && islower (c1) && isupper (c2)) - r += "_"; - } - r += toupper (g[g.size () - 1]); - - return ctx.escape (r); - } - void open (ifstream& ifs, string const& path) { @@ -88,7 +64,10 @@ namespace } void generator:: -generate (options const& ops, semantics::unit& unit, path const& p) +generate (options const& ops, + features& fts, + semantics::unit& unit, + path const& p) { try { @@ -98,7 +77,7 @@ generate (options const& ops, semantics::unit& unit, path const& p) if (ops.generate_schema ()) { - auto_ptr ctx (create_context (cerr, unit, ops, 0)); + auto_ptr ctx (create_context (cerr, unit, ops, fts, 0)); switch (ops.database ()) { @@ -249,9 +228,10 @@ generate (options const& ops, semantics::unit& unit, path const& p) sloc_filter sloc (hxx); ind_filter ind (hxx); - auto_ptr ctx (create_context (hxx, unit, ops, model.get ())); + auto_ptr ctx ( + create_context (hxx, unit, ops, fts, model.get ())); - string guard (make_guard (gp + hxx_name, *ctx)); + string guard (ctx->make_guard (gp + hxx_name)); hxx << "#ifndef " << guard << endl << "#define " << guard << endl @@ -328,7 +308,8 @@ generate (options const& ops, semantics::unit& unit, path const& p) sloc_filter sloc (ixx); ind_filter ind (ixx); - auto_ptr ctx (create_context (ixx, unit, ops, model.get ())); + auto_ptr ctx ( + create_context (ixx, unit, ops, fts, model.get ())); // Copy prologue. // @@ -376,7 +357,8 @@ generate (options const& ops, semantics::unit& unit, path const& p) sloc_filter sloc (cxx); ind_filter ind (cxx); - auto_ptr ctx (create_context (cxx, unit, ops, model.get ())); + auto_ptr ctx ( + create_context (cxx, unit, ops, fts, model.get ())); cxx << "#include " << endl << endl; @@ -432,7 +414,8 @@ generate (options const& ops, semantics::unit& unit, path const& p) sloc_filter sloc (sch); ind_filter ind (sch); - auto_ptr ctx (create_context (sch, unit, ops, model.get ())); + auto_ptr ctx ( + create_context (sch, unit, ops, fts, model.get ())); // Copy prologue. // @@ -487,7 +470,8 @@ generate (options const& ops, semantics::unit& unit, path const& p) // if (sql_schema) { - auto_ptr ctx (create_context (sql, unit, ops, model.get ())); + auto_ptr ctx ( + create_context (sql, unit, ops, fts, model.get ())); // Copy prologue. // diff --git a/odb/generator.hxx b/odb/generator.hxx index 4911883..5a1bbdf 100644 --- a/odb/generator.hxx +++ b/odb/generator.hxx @@ -6,6 +6,7 @@ #define ODB_GENERATOR_HXX #include +#include #include class generator @@ -14,7 +15,10 @@ public: class failed {}; void - generate (options const&, semantics::unit&, semantics::path const&); + generate (options const&, + features&, + semantics::unit&, + semantics::path const&); generator () {} diff --git a/odb/header.cxx b/odb/header.cxx index d316036..a2b3b96 100644 --- a/odb/header.cxx +++ b/odb/header.cxx @@ -31,14 +31,12 @@ namespace header // the latter is just a using-declaration for the former. To resolve // this we will include TR1 traits if the Boost TR1 header is included. // - if (ctx.unit.count ("tr1-pointer-used") && - ctx.unit.get ("tr1-pointer-used")) + if (ctx.features.tr1_pointer) { os << "#include " << endl << "#include " << endl; } - else if (ctx.unit.count ("boost-pointer-used") && - ctx.unit.get ("boost-pointer-used")) + else if (ctx.features.boost_pointer) { os << "#ifdef BOOST_TR1_MEMORY_HPP_INCLUDED" << endl << "# include " << endl @@ -48,9 +46,26 @@ namespace header os << "#include " << endl; + if (ctx.features.polymorphic_object) + os << "#include " << endl; + if (ctx.options.generate_query ()) + { os << "#include " << endl; + if (ctx.features.simple_object) + os << "#include " << endl; + + if (ctx.features.polymorphic_object) + os << "#include " << endl; + + if (ctx.features.no_id_object) + os << "#include " << endl; + + if (ctx.features.view) + os << "#include " << endl; + } + os << endl; } } diff --git a/odb/plugin.cxx b/odb/plugin.cxx index 599760f..73ada7a 100644 --- a/odb/plugin.cxx +++ b/odb/plugin.cxx @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -115,24 +116,26 @@ gate_callback (void*, void*) parser p (*options_, loc_pragmas_, ns_loc_pragmas_, decl_pragmas_); auto_ptr u (p.parse (global_namespace, file_)); + features f; + // Validate, pass 1. // validator v; - v.validate (*options_, *u, file_, 1); + v.validate (*options_, f, *u, file_, 1); // Process. // processor pr; - pr.process (*options_, *u, file_); + pr.process (*options_, f, *u, file_); // Validate, pass 2. // - v.validate (*options_, *u, file_, 2); + v.validate (*options_, f, *u, file_, 2); // Generate. // generator g; - g.generate (*options_, *u, file_); + g.generate (*options_, f, *u, file_); } catch (pragmas_failed const&) { diff --git a/odb/pragma.cxx b/odb/pragma.cxx index 080de98..698551a 100644 --- a/odb/pragma.cxx +++ b/odb/pragma.cxx @@ -460,7 +460,8 @@ check_spec_decl_type (tree d, p == "callback" || p == "query" || p == "object" || - p == "optimistic") + p == "optimistic" || + p == "polymorphic") { if (tc != RECORD_TYPE) { @@ -881,6 +882,18 @@ handle_pragma (cpp_reader* reader, tt = pragma_lex (&t); } + else if (p == "polymorphic") + { + // polymorphic + // + + // Make sure we've got the correct declaration type. + // + if (decl != 0 && !check_spec_decl_type (decl, decl_name, p, loc)) + return; + + tt = pragma_lex (&t); + } else if (p == "callback") { // callback (name) diff --git a/odb/processor.cxx b/odb/processor.cxx index 981cb31..0299ad5 100644 --- a/odb/processor.cxx +++ b/odb/processor.cxx @@ -12,13 +12,16 @@ using namespace std; void processor:: -process (options const& ops, semantics::unit& unit, semantics::path const&) +process (options const& ops, + features& f, + semantics::unit& unit, + semantics::path const&) { try { // Process types. // - auto_ptr ctx (create_context (cerr, unit, ops, 0)); + auto_ptr ctx (create_context (cerr, unit, ops, f, 0)); relational::process (); } catch (operation_failed const&) diff --git a/odb/processor.hxx b/odb/processor.hxx index fdf8938..22f2d86 100644 --- a/odb/processor.hxx +++ b/odb/processor.hxx @@ -6,6 +6,7 @@ #define ODB_PROCESSOR_HXX #include +#include #include class processor @@ -14,7 +15,10 @@ public: class failed {}; void - process (options const&, semantics::unit&, semantics::path const&); + process (options const&, + features&, + semantics::unit&, + semantics::path const&); processor () {} diff --git a/odb/relational/common.cxx b/odb/relational/common.cxx index 381d77f..914a778 100644 --- a/odb/relational/common.cxx +++ b/odb/relational/common.cxx @@ -14,6 +14,122 @@ using namespace std; namespace relational { + // query_alias_traits + // + + void query_alias_traits:: + traverse_object (semantics::class_& c) + { + // We don't want to traverse bases. + // + names (c); + } + + void query_alias_traits:: + traverse_pointer (semantics::data_member& m, semantics::class_& c) + { + // Ignore polymorphic id references. + // + if (m.count ("polymorphic-ref")) + return; + + // Come up with a table alias. Generally, we want it to be based + // on the column name. This is straightforward for single-column + // references. In case of a composite id, we will need to use the + // column prefix which is based on the data member name, unless + // overridden by the user. In the latter case the prefix can be + // empty, in which case we will just fall back on the member's + // public name. + // + string alias; + + if (composite_wrapper (utype ((*id_member (c))))) + { + string p (column_prefix (m, key_prefix_, default_name_)); + + if (p.empty ()) + p = public_name_db (m); + else + p.resize (p.size () - 1); // Remove trailing underscore. + + alias = column_prefix_ + p; + } + else + alias = column_prefix_ + column_name (m, key_prefix_, default_name_); + + generate (alias, c); + } + + void query_alias_traits:: + generate (string const& alias, semantics::class_& c) + { + string tag (escape (alias + "_alias_tag")); + + if (tags_.find (tag) == tags_.end ()) + { + os << "class " << tag << ";" + << endl; + + tags_.insert (tag); + } + + // Generate the alias_traits specialization. + // + generate_specialization (alias, tag, c); + } + + void query_alias_traits:: + generate_specialization (string const& alias, + string const& tag, + semantics::class_& c) + { + string const& fq_name (class_fq_name (c)); + string guard ( + make_guard ( + "ODB_ALIAS_TRAITS_" + alias + "_FOR_" + flat_name (fq_name))); + + if (specs_.find (guard) != specs_.end ()) + return; + else + specs_.insert (guard); + + semantics::class_* poly_root (polymorphic (c)); + bool poly_derived (poly_root != 0 && poly_root != &c); + semantics::class_* poly_base (poly_derived ? &polymorphic_base (c) : 0); + + if (poly_derived) + generate_specialization (alias, tag, *poly_base); + + os << "#ifndef " << guard << endl + << "#define " << guard << endl; + + os << "template " << endl + << "struct alias_traits< " << fq_name << ", " << tag << ", d >" + << "{" + << "static const char table_name[];"; + + if (poly_derived) + os << "typedef alias_traits< " << class_fq_name (*poly_base) << ", " << + tag << " > base_traits;"; + + os << "};"; + + os << "template " << endl + << "const char alias_traits< " << fq_name << ", " << tag << + ", d >::" << endl + << "table_name[] = "; + + if (poly_root != 0) + os << strlit (quote_id (alias + "_" + table_name (c).uname ())); + else + os << strlit (quote_id (alias)); + + os << ";" + << "#endif // " << guard << endl + << endl; + } + + // query_columns_base // @@ -81,31 +197,19 @@ namespace relational void query_columns_base:: traverse_pointer (semantics::data_member& m, semantics::class_& c) { + // Ignore polymorphic id references. + // + if (m.count ("polymorphic-ref")) + return; + string name (public_name (m)); bool inv (inverse (m, key_prefix_)); if (decl_) { os << "// " << name << endl - << "//" << endl - << "static const char " << name << "_alias_[];" - << endl; + << "//" << endl; - if (inv) - { - os << "typedef" << endl - << "odb::query_pointer<" << endl - << " odb::pointer_query_columns<" << endl - << " " << class_fq_name (c) << "," << endl - << " " << name << "_alias_ > >" << endl - << name << "_type_ ;" - << endl - << "static const " << name << "_type_ " << name << ";" - << endl; - } - } - else - { // Come up with a table alias. Generally, we want it to be based // on the column name. This is straightforward for single-column // references. In case of a composite id, we will need to use the @@ -130,11 +234,30 @@ namespace relational else alias = column_prefix_ + column_name (m, key_prefix_, default_name_); - os << "const char " << scope_ << "::" << name << "_alias_[] = " << - strlit (quote_id (alias)) << ";" + string tag (escape (alias + "_alias_tag")); + string const& fq_name (class_fq_name (c)); + + os << "typedef" << endl + << "odb::alias_traits< " << fq_name << ", " << tag << " >" << endl + << name << "_alias_;" << endl; if (inv) + { + os << "typedef" << endl + << "odb::query_pointer<" << endl + << " odb::pointer_query_columns<" << endl + << " " << fq_name << "," << endl + << " " << name << "_alias_ > >" << endl + << name << "_type_ ;" + << endl + << "static const " << name << "_type_ " << name << ";" + << endl; + } + } + else + { + if (inv) os << "const " << scope_ << "::" << name << "_type_" << endl << scope_ << "::" << name << ";" << endl; @@ -155,7 +278,7 @@ namespace relational : ptr_ (ptr), decl_ (false), in_ptr_ (false) { scope_ = ptr ? "pointer_query_columns" : "query_columns"; - scope_ += "< " + class_fq_name (c) + ", table >"; + scope_ += "< " + class_fq_name (c) + ", A >"; } void query_columns:: @@ -217,7 +340,7 @@ namespace relational // Composite member. Note that here we don't use suffix. // - os << "template " << endl + os << "template " << endl << "const typename " << scope_ << "::" << name << "_type_" << endl << scope_ << "::" << name << ";" << endl; @@ -261,10 +384,10 @@ namespace relational { // Note that here we don't use suffix. // - os << "template " << endl + os << "template " << endl << "const typename " << scope_ << "::" << name << "_type_" << endl - << scope_ << "::" << name << " (" << "table, " << - strlit (quote_id (column)); + << scope_ << "::" << endl + << name << " (A::" << "table_name, " << strlit (quote_id (column)); column_ctor_extra (m); @@ -295,6 +418,11 @@ namespace relational void query_columns:: traverse_pointer (semantics::data_member& m, semantics::class_& c) { + // Ignore polymorphic id references. + // + if (m.count ("polymorphic-ref")) + return; + // If this is for the pointer_query_columns and the member is not // inverse, then create the normal member corresponding to the id // column. This will allow the user to check it for NULL or to diff --git a/odb/relational/common.hxx b/odb/relational/common.hxx index 276868c..26e8197 100644 --- a/odb/relational/common.hxx +++ b/odb/relational/common.hxx @@ -6,6 +6,7 @@ #define ODB_RELATIONAL_COMMON_HXX #include +#include #include // std::size_t #include #include @@ -221,6 +222,35 @@ namespace relational } }; + // Generate alias tags and alias_traits specializations for pointers + // in this objects. + // + struct query_alias_traits: object_columns_base, virtual context + { + typedef query_alias_traits base; + + query_alias_traits (std::set& tags, std::set& specs) + : tags_ (tags), specs_ (specs) {} + + virtual void + traverse_object (semantics::class_&); + + virtual void + traverse_pointer (semantics::data_member&, semantics::class_&); + + virtual void + generate (string const& alias, semantics::class_&); + + private: + void + generate_specialization (string const& alias, + string const& tag, + semantics::class_&); + + std::set& tags_; + std::set& specs_; + }; + // // struct query_columns_base: object_columns_base, virtual context diff --git a/odb/relational/context.cxx b/odb/relational/context.cxx index 98bbfe4..8630a30 100644 --- a/odb/relational/context.cxx +++ b/odb/relational/context.cxx @@ -27,6 +27,7 @@ namespace relational need_alias_as (current ().need_alias_as), insert_send_auto_id (current ().insert_send_auto_id), delay_freeing_statement_result (current ().delay_freeing_statement_result), + need_image_clone (current ().need_image_clone), bind_vector (data_->bind_vector_), truncated_vector (data_->truncated_vector_) { diff --git a/odb/relational/context.hxx b/odb/relational/context.hxx index 52cb9c0..60ed934 100644 --- a/odb/relational/context.hxx +++ b/odb/relational/context.hxx @@ -159,6 +159,7 @@ namespace relational bool need_alias_as; bool insert_send_auto_id; bool delay_freeing_statement_result; + bool need_image_clone; string const& bind_vector; string const& truncated_vector; diff --git a/odb/relational/header.cxx b/odb/relational/header.cxx index ccd73bf..46ba722 100644 --- a/odb/relational/header.cxx +++ b/odb/relational/header.cxx @@ -7,65 +7,790 @@ using namespace std; -namespace relational +void relational::header::class1:: +traverse_object (type& c) { - namespace header + 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. + + 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)); + column_count_type const& cc (column_count (c)); + + os << "// " << class_name (c) << endl + << "//" << endl; + + // class_traits + // + os << "template <>" << endl + << "struct class_traits< " << type << " >" + << "{" + << "static const class_kind kind = class_object;" + << "};"; + + // pointer_query_columns & query_columns + // + if (options.generate_query ()) + { + // If we don't have object pointers, then also generate + // query_columns (in this case pointer_query_columns and + // query_columns are the same and the former inherits from + // the latter). Otherwise we have to postpone query_columns + // generation until the second pass to deal with forward- + // declared objects. + // + if (!has_a (c, test_pointer | include_base)) + query_columns_type_->traverse (c); + + pointer_query_columns_type_->traverse (c); + } + + // object_traits + // + os << "template <>" << endl + << "class access::object_traits< " << type << " >" + << "{" + << "public:" << endl; + + object_public_extra_pre (c); + + // 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") << ";"; + + if (poly) { - void - generate () + os << "typedef " << class_fq_name (*poly_root) << " root_type;" + << "typedef object_traits root_traits;"; + + if (poly_derived) { - context ctx; - ostream& os (ctx.os); + os << "typedef " << class_fq_name (*poly_base) << " base_type;" + << "typedef object_traits base_traits;" + << "typedef root_traits::discriminator_type discriminator_type;" + << "typedef odb::polymorphic_concrete_info info_type;"; + + if (abst) + os << "typedef odb::polymorphic_abstract_info " << + "abstract_info_type;"; + else + os << "typedef odb::polymorphic_entry entry_type;"; + + // Calculate our hierarchy depth (number of classes). + // + size_t depth (polymorphic_depth (c)); + + os << "static const std::size_t depth = " << depth << "UL;" + << endl; - instance i; - i->generate (); + os << "static " << (abst ? "abstract_" : "") << "info_type info;"; - os << "namespace odb" + if (!abst) + os << "static entry_type entry;"; + } + else + { + semantics::names* hint; + semantics::type& t (utype (*discriminator, hint)); + + os << "typedef " << t.fq_name (hint) << " discriminator_type;" + << endl; + + os << "struct discriminator_image_type" << "{"; - { - traversal::unit unit; - traversal::defines unit_defines; - typedefs unit_typedefs (false); - traversal::namespace_ ns; - instance c; + discriminator_image_member_->traverse (*discriminator); + + if (optimistic != 0) + version_image_member_->traverse (*optimistic); - unit >> unit_defines >> ns; - unit_defines >> c; - unit >> unit_typedefs >> c; + os << "std::size_t version;" + << "};"; - traversal::defines ns_defines; - typedefs ns_typedefs (false); + os << "typedef odb::polymorphic_map map_type;" + << "typedef odb::polymorphic_concrete_info info_type;"; - ns >> ns_defines >> ns; - ns_defines >> c; - ns >> ns_typedefs >> c; + if (abst) + os << "typedef odb::polymorphic_abstract_info " << + "abstract_info_type;"; + else + os << "typedef odb::polymorphic_entry entry_type;"; - unit.dispatch (ctx.unit); + os << "static const std::size_t depth = 1UL;" + << endl; + + os << "static map_type* map;" + << "static " << (abst ? "abstract_" : "") << "info_type info;"; + + if (!abst) + os << "static entry_type entry;"; + } + } + + os << endl; + + // id_type & id_image_type + // + 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;"; + + if (poly_derived) + os << "typedef object_traits< " << type << " >:: id_image_type " << + "id_image_type;" + << "static const bool auto_id = false;" + << endl; + else + os << "typedef object_traits< " << type << " >::id_image_type " << + "id_image_type;" + << "static const bool auto_id = object_traits< " << type << + " >::auto_id;" + << endl; + } + else + { + { + semantics::names* hint; + semantics::type& t (utype (*id, hint)); + os << "typedef " << t.fq_name (hint) << " id_type;"; } + if (optimistic != 0) { - traversal::unit unit; - traversal::defines unit_defines; - typedefs unit_typedefs (false); - traversal::namespace_ ns; - instance c; + semantics::names* hint; + semantics::type& t (utype (*optimistic, hint)); + os << "typedef " << t.fq_name (hint) << " version_type;"; + } + + os << "static const bool auto_id = " << (auto_id ? "true;" : "false;") + << endl; - unit >> unit_defines >> ns; - unit_defines >> c; - unit >> unit_typedefs >> c; + os << "struct id_image_type" + << "{"; - traversal::defines ns_defines; - typedefs ns_typedefs (false); + id_image_member_->traverse (*id); - ns >> ns_defines >> ns; - ns_defines >> c; - ns >> ns_typedefs >> c; + if (optimistic != 0) + version_image_member_->traverse (*optimistic); - unit.dispatch (ctx.unit); - } + os << "std::size_t version;" + << "};"; + } + } + else if (!reuse_abst) + { + // Object without id. + // + os << "typedef void id_type;" + << "static const bool auto_id = false;" + << endl; + } + + // abstract + // + os << "static const bool abstract = " << (abst ? "true" : "false") << ";" + << endl; + + // image_type + // + image_type_->traverse (c); + + // + // Containers (abstract and concrete). + // + + { + instance t (c); + t->traverse (c); + } + + // + // Functions (abstract and concrete). + // + + // 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 (!poly_derived && id != 0) + { + if (options.generate_query ()) + os << "static id_type" << endl + << "id (const image_type&);" + << endl; + + if (optimistic != 0) + os << "static version_type" << endl + << "version (const image_type&);" + << endl; + } + + // discriminator() + // + if (poly && !poly_derived) + os << "static discriminator_type" << endl + << "discriminator (const image_type&);" + << endl; + + // grow () + // + if (generate_grow) + { + // For derived classes in a polymorphic hierarchy, grow() will + // check bases up to the specified depth. If one of the base + // images has grown, then it will increment its version. But + // the return value only indicates the state of this image, + // excluding polymorphic bases (in other words, it is possible + // that one of the bases has grown but this function returns + // false). + // + os << "static bool" << endl + << "grow (image_type&, " << truncated_vector; + + if (poly_derived) + os << ", std::size_t = depth"; + + os << ");" + << endl; + } + + // bind (image_type) + // + os << "static void" << endl + << "bind (" << bind_vector << "," << endl; + + // If we are a derived type in a polymorphic hierarchy, then + // we get the the external id binding. + // + if (poly_derived) + os << "const " << bind_vector << " id," << endl + << "std::size_t id_size," << endl; + + os << "image_type&," << endl + << db << "::statement_kind);" + << endl; + + // bind (id_image_type) + // + if (id != 0) + { + os << "static void" << endl + << "bind (" << bind_vector << ", id_image_type&" << + (optimistic != 0 ? ", bool bind_version = true" : "") << ");" + << endl; + } + + // init (image, object) + // + os << "static bool" << endl + << "init (image_type&, const object_type&, " << db << "::statement_kind);" + << endl; + + // init (object, image) + // + os << "static void" << endl + << "init (object_type&, const image_type&, database*"; + + if (poly_derived) + os << ", std::size_t = depth"; + + os << ");" + << endl; + + // init (id_image, id) + // + if (id != 0) + { + os << "static void" << endl + << "init (id_image_type&, const id_type&" << + (optimistic != 0 ? ", const version_type* = 0" : "") << ");" + << endl; + } + + if (poly_derived) + { + // check_version + // + os << "static bool" << endl + << "check_version (const std::size_t*, const image_type&);" + << endl; + + // update_version + // + os << "static void" << endl + << "update_version (std::size_t*, const image_type&, " << + db << "::binding*);" + << endl; + } + + // The rest does not apply to reuse-abstract objects. + // + if (reuse_abst) + { + object_public_extra_post (c); + os << "};"; + return; + } + + // Cache traits typedefs. + // + if (id == 0) + { + os << "typedef" << endl + << "odb::no_id_pointer_cache_traits" << endl + << "pointer_cache_traits;" + << "typedef" << endl + << "odb::no_id_reference_cache_traits" << endl + << "reference_cache_traits;" + << endl; + } + else if (poly_derived) + { + os << "typedef" << endl + << "odb::pointer_cache_traits" << endl + << "pointer_cache_traits;" + << "typedef" << endl + << "odb::reference_cache_traits" << endl + << "reference_cache_traits;" + << endl; + } + else + { + os << "typedef" << endl + << "odb::pointer_cache_traits" << endl + << "pointer_cache_traits;" + << "typedef" << endl + << "odb::reference_cache_traits" << endl + << "reference_cache_traits;" + << endl; + } + + // Statements typedefs. + // + if (poly) + { + if (poly_derived) + os << "typedef" << endl + << db << "::polymorphic_derived_object_statements" << + "" << endl + << "statements_type;" + << "typedef" << endl + << db << "::polymorphic_root_object_statements" << endl + << "root_statements_type;" + << endl; + else + os << "typedef" << endl + << db << "::polymorphic_root_object_statements" << endl + << "statements_type;" + << "typedef statements_type root_statements_type;" + << "typedef " << db << "::object_statements " << + "base_statements_type;" + << endl; + } + else + { + if (id != 0) + os << "typedef " << db << "::object_statements " << + "statements_type;" + << endl; + else + os << "typedef " << db << "::no_id_object_statements " << + "statements_type;" + << endl; + } + + // + // Query (concrete). + // + + if (options.generate_query ()) + { + // query_base_type + // + os << "typedef " << db << "::query query_base_type;" + << endl; + + // query_type + // + os << "struct query_type;"; + } + + // + // Containers (concrete). + // + + // Statement cache (forward declaration). + // + if (id != 0) + os << "struct container_statement_cache_type;" + << endl; + + // column_count + // + os << "static const std::size_t column_count = " << cc.total << "UL;" + << "static const std::size_t id_column_count = " << cc.id << "UL;" + << "static const std::size_t inverse_column_count = " << + cc.inverse << "UL;" + << "static const std::size_t readonly_column_count = " << + cc.readonly << "UL;" + << "static const std::size_t managed_optimistic_column_count = " << + cc.optimistic_managed << "UL;"; + + if (poly && !poly_derived) + os << "static const std::size_t discriminator_column_count = " << + cc.discriminator << "UL;"; + + os << endl; + + // Statements. + // + os << "static const char persist_statement[];"; + + if (id != 0) + { + if (poly_derived) + { + char const* n (abst ? "1" : "depth"); + + os << "static const char* const find_statements[" << n << "];" + << "static const std::size_t find_column_counts[" << n << "];"; + } + else + { + os << "static const char find_statement[];"; - os << "}"; + if (poly) + os << "static const char find_discriminator_statement[];"; } + + if (cc.total != cc.id + cc.inverse + cc.readonly) + os << "static const char update_statement[];"; + + os << "static const char erase_statement[];"; + + if (optimistic != 0 && !poly_derived) + os << "static const char optimistic_erase_statement[];"; + } + + if (options.generate_query ()) + { + os << "static const char query_statement[];" + << "static const char erase_query_statement[];" + << endl + << "static const char table_name[];"; + } + + os << endl; + + // + // 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 + << "persist (database&, " << (auto_id ? "" : "const ") << "object_type&"; + + if (poly) + os << ", bool top = true, bool dyn = true"; + + os << ");" + << endl; + + if (id != 0) + { + // find (id) + // + if (c.default_ctor ()) + os << "static pointer_type" << endl + << "find (database&, const id_type&);" + << endl; + + // find (id, obj) + // + os << "static bool" << endl + << "find (database&, const id_type&, object_type&"; + + if (poly) + os << ", bool dyn = true"; + + os << ");" + << endl; + + // reload () + // + os << "static bool" << endl + << "reload (database&, object_type&"; + + if (poly) + os << ", bool dyn = true"; + + os << ");" + << endl; + + // update () + // + // In case of a polymorphic object, we generate update() even if it is + // readonly since the potentially-readwrite base will rely on it to + // initialize the id image. + // + // + if (!readonly (c) || poly) + { + os << "static void" << endl + << "update (database&, const object_type&"; + + if (poly) + os << ", bool top = true, bool dyn = true"; + + os << ");" + << endl; + } + + // erase () + // + os << "static void" << endl + << "erase (database&, const id_type&"; + + if (poly) + os << ", bool top = true, bool dyn = true"; + + os << ");" + << endl; + + os << "static void" << endl + << "erase (database&, const object_type&"; + + if (poly) + os << ", bool top = true, bool dyn = true"; + + os << ");" + << endl; + } + + // query () + // + if (options.generate_query ()) + { + os << "static result" << endl + << "query (database&, const query_base_type&);" + << endl; + + os << "static unsigned long long" << endl + << "erase_query (database&, const query_base_type&);" + << endl; + } + + // create_schema () + // + if (embedded_schema || separate_schema) + { + os << "static bool" << endl + << "create_schema (database&, unsigned short pass, bool drop);" + << endl; + } + + object_public_extra_post (c); + + // Implementation details. + // + os << "public:" << endl; + + if (id != 0) + { + // Load the object image. + // + os << "static bool" << endl + << "find_ ("; + + if (poly && !poly_derived) + os << "base_statements_type&, "; + else + os << "statements_type&, "; + + os << "const id_type*"; + + if (poly_derived && !abst) + os << ", std::size_t = depth"; + + os << ");" + << endl; + + // Load the rest of the object (containers, etc). Expects the id + // image in the object statements to be initialized to the object + // id. + // + os << "static void" << endl + << "load_ ("; + + if (poly && !poly_derived) + os << "base_statements_type&, "; + else + os << "statements_type&, "; + + os << "object_type&"; + + if (poly_derived) + os << ", std::size_t = depth"; + + os << ");" + << endl; } + + // discriminator_ () + // + if (poly && !poly_derived) + { + os << "static void" << endl + << "discriminator_ (statements_type&," << endl + << "const id_type&," << endl + << "discriminator_type*"; + + if (optimistic != 0) + os << "," << endl + << "version_type* = 0"; + + os << ");" + << endl; + } + + // Load the dynamic part of the object. Depth inidicates where + // the dynamic part starts. Expects the id image in the object + // statements to be initialized to the object id. We don't need + // it if we are poly-abstract. + // + if (poly_derived && !abst) + os << "static void" << endl + << "load_ (database&, root_type&, std::size_t);" + << endl; + + // Image chain manipulation. + // + if (poly && need_image_clone && options.generate_query ()) + { + os << "static root_traits::image_type&" << endl + << "root_image (image_type&);" + << endl; + + os << "static image_type*" << endl + << "clone_image (const image_type&);" + << endl; + + os << "static void" << endl + << "copy_image (image_type&, const image_type&);" + << endl; + + os << "static void" << endl + << "free_image (image_type*);" + << endl; + } + + os << "};"; +} + +void relational::header:: +generate () +{ + context ctx; + ostream& os (ctx.os); + + instance i; + i->generate (); + + os << "namespace odb" + << "{"; + + { + traversal::unit unit; + traversal::defines unit_defines; + typedefs unit_typedefs (false); + traversal::namespace_ ns; + instance 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; + + unit.dispatch (ctx.unit); + } + + { + traversal::unit unit; + traversal::defines unit_defines; + typedefs unit_typedefs (false); + traversal::namespace_ ns; + instance 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; + + unit.dispatch (ctx.unit); + } + + os << "}"; } diff --git a/odb/relational/header.hxx b/odb/relational/header.hxx index 250a07c..3d3189e 100644 --- a/odb/relational/header.hxx +++ b/odb/relational/header.hxx @@ -96,20 +96,36 @@ namespace relational virtual void traverse (type& c) { + type* poly_root (polymorphic (c)); + bool poly_derived (poly_root != 0 && poly_root != &c); + os << "struct image_type"; if (!view (c)) { - instance b; - traversal::inherits i (*b); - inherits (c, i); + // Don't go into the base if we are a derived type in a + // polymorphic hierarchy. + // + if (!poly_derived) + { + instance b; + traversal::inherits i (*b); + inherits (c, i); + } } os << "{"; + if (poly_derived) + os << "object_traits::image_type* base;" + << endl; + names (c); - if (!(composite (c) || abstract (c))) + // We don't need a version if this is a composite value type + // or reuse-abstract object. + // + if (!(composite (c) || (abstract (c) && !polymorphic (c)))) os << "std::size_t version;" << endl; @@ -146,7 +162,8 @@ namespace relational if (first_) { - os << ": "; + os << ":" << endl + << " "; first_ = false; } else @@ -155,8 +172,17 @@ namespace relational << " "; } - os << (ptr_ ? "pointer_query_columns" : "query_columns") << - "< " << class_fq_name (c) << ", table >"; + os << (ptr_ ? "pointer_query_columns" : "query_columns") << "< " << + class_fq_name (c) << ", "; + + // If our base is polymorphic, then it has its own table/alias. + // + if (polymorphic (c)) + os << "typename A::base_traits"; + else + os << "A"; + + os << " >"; } private: @@ -185,10 +211,17 @@ namespace relational os << "// " << name << endl << "//" << endl - << "typedef " << - (ptr_ ? "pointer_query_columns" : "query_columns") << - "< " << class_fq_name (c) << ", table > " << name << ";" - << endl; + << "typedef " << + (ptr_ ? "pointer_query_columns" : "query_columns") << "< " << + class_fq_name (c) << ", "; + + if (polymorphic (c)) + os << "typename A::base_traits"; + else + os << "A"; + + os << " > " << name << ";" + << endl; } private: @@ -212,16 +245,16 @@ namespace relational if (ptr_) { - os << "template " << endl - << "struct pointer_query_columns< " << type << ", table >"; + os << "template " << endl + << "struct pointer_query_columns< " << type << ", A >"; // If we don't have pointers (in the whole hierarchy), then // pointer_query_columns and query_columns are the same. // - if (!has_a (c, test_pointer)) + if (!has_a (c, test_pointer | include_base)) { os << ":" << endl - << " query_columns< " << type << ", table >" + << " query_columns< " << type << ", A >" << "{" << "};"; } @@ -260,6 +293,10 @@ namespace relational if (has_ptr) { + // This class contains everything for inverse pointers and + // aliases for non-inverse ones. It doesn't depend on the + // table alias (A) template argument. + // os << "template <>" << endl << "struct query_columns_base< " << type << " >" << "{"; @@ -270,8 +307,8 @@ namespace relational os << "};"; } - os << "template " << endl - << "struct query_columns< " << type << ", table >"; + os << "template " << endl + << "struct query_columns< " << type << ", A >"; if (has_ptr) os << ":" << endl @@ -352,20 +389,20 @@ namespace relational using semantics::class_; // Figure out if this member is from a base object or composite - // value and whether it is abstract. + // value and if it's from an object, whether it is reuse-abstract. // - bool base, abst; + bool base, reuse_abst; if (object (c_)) { base = cur_object != &c_ || !object (dynamic_cast (m.scope ())); - abst = abstract (c_); + reuse_abst = abstract (c_) && !polymorphic (c_); } else { - base = false; // We don't go into bases. - abst = true; // Always abstract. + base = false; // We don't go into bases. + reuse_abst = true; // Always abstract. } container_kind_type ck (container_kind (c)); @@ -407,7 +444,7 @@ namespace relational // size_t id_columns, data_columns, cond_columns; - if (!abst) + if (!reuse_abst) { type& idt (container_idt (m)); @@ -513,7 +550,7 @@ namespace relational container_public_extra_pre (m); - if (!abst) + if (!reuse_abst) { // column_count // @@ -572,8 +609,9 @@ namespace relational os << " container_type;"; - os << "typedef odb::access::container_traits< container_type > " << - "container_traits_type;"; + os << "typedef" << endl + << "odb::access::container_traits" << endl + << "container_traits_type;"; switch (ck) { @@ -912,6 +950,7 @@ namespace relational class1 () : id_image_member_ ("id_"), version_image_member_ ("version_"), + discriminator_image_member_ ("discriminator_"), query_columns_type_ (false), pointer_query_columns_type_ (true) { @@ -922,6 +961,7 @@ namespace relational context (), id_image_member_ ("id_"), version_image_member_ ("version_"), + discriminator_image_member_ ("discriminator_"), query_columns_type_ (false), pointer_query_columns_type_ (true) { @@ -952,396 +992,7 @@ namespace relational } virtual void - traverse_object (type& c) - { - bool abstract (context::abstract (c)); - string const& type (class_fq_name (c)); - - semantics::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. - - semantics::data_member* optimistic (context::optimistic (c)); - - column_count_type const& cc (column_count (c)); - - os << "// " << class_name (c) << endl - << "//" << endl; - - // class_traits - // - os << "template <>" << endl - << "struct class_traits< " << type << " >" - << "{" - << "static const class_kind kind = class_object;" - << "};"; - - // pointer_query_columns & query_columns - // - if (options.generate_query ()) - { - // If we don't have object pointers, then also generate - // query_columns (in this case pointer_query_columns and - // query_columns are the same and the former inherits from - // the latter). Otherwise we have to postpone query_columns - // generation until the second pass to deal with forward- - // declared objects. - // - if (!has_a (c, test_pointer)) - query_columns_type_->traverse (c); - - pointer_query_columns_type_->traverse (c); - } - - // object_traits - // - os << "template <>" << endl - << "class access::object_traits< " << type << " >" - << "{" - << "public:" << endl; - - object_public_extra_pre (c); - - // object_type & pointer_type - // - os << "typedef " << type << " object_type;" - << "typedef " << c.get ("object-pointer") << " pointer_type;"; - - // id_type & id_image_type - // - 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 - << "static const bool auto_id = object_traits< " << type << - " >::auto_id;" - << endl - << "typedef object_traits< " << type << " >::id_image_type " << - "id_image_type;" - << endl; - } - 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") << ";" - << endl; - - os << "struct id_image_type" - << "{"; - - id_image_member_->traverse (*id); - - if (optimistic != 0) - version_image_member_->traverse (*optimistic); - - os << "std::size_t version;" - << "};"; - } - } - else if (!abstract) - { - // Object without id. - // - os << "typedef void id_type;" - << endl - << "static const bool auto_id = false;" - << endl; - } - - // image_type - // - image_type_->traverse (c); - - // - // Containers (abstract and concrete). - // - - { - instance t (c); - t->traverse (c); - } - - // - // Functions (abstract and concrete). - // - - // id () - // - if (id != 0 || !abstract) - // 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 (id != 0 && options.generate_query ()) - os << "static id_type" << endl - << "id (const image_type&);" - << endl; - - if (id != 0 && optimistic != 0) - os << "static version_type" << endl - << "version (const image_type&);" - << endl; - - // grow () - // - if (generate_grow) - { - os << "static bool" << endl - << "grow (image_type&, " << truncated_vector << ");" - << endl; - } - - // bind (image_type) - // - os << "static void" << endl - << "bind (" << bind_vector << ", image_type&, " - << db << "::statement_kind);" - << endl; - - // bind (id_image_type) - // - if (id != 0) - { - os << "static void" << endl - << "bind (" << bind_vector << ", id_image_type&);" - << endl; - } - - // init (image, object) - // - os << "static bool" << endl - << "init (image_type&, const object_type&, " << - db << "::statement_kind);" - << endl; - - // init (object, image) - // - os << "static void" << endl - << "init (object_type&, const image_type&, database*);" - << endl; - - // init (id_image, id) - // - if (id != 0) - { - os << "static void" << endl - << "init (id_image_type&, const id_type&" << - (optimistic != 0 ? ", const version_type* = 0" : "") << ");" - << endl; - } - - // - // The rest only applies to concrete objects. - // - if (abstract) - { - object_public_extra_post (c); - os << "};"; - return; - } - - // - // Query (concrete). - // - - if (options.generate_query ()) - { - // query_base_type - // - os << "typedef " << db << "::query query_base_type;" - << endl; - - // query_type - // - os << "struct query_type;"; - } - - // - // Containers (concrete). - // - - // Statement cache (forward declaration). - // - if (id != 0) - os << "struct container_statement_cache_type;" - << endl; - - // column_count - // - os << "static const std::size_t column_count = " << cc.total << "UL;" - << "static const std::size_t id_column_count = " << cc.id << "UL;" - << "static const std::size_t inverse_column_count = " << - cc.inverse << "UL;" - << "static const std::size_t readonly_column_count = " << - cc.readonly << "UL;" - << "static const std::size_t managed_optimistic_column_count = " << - cc.optimistic_managed << "UL;" - << endl; - - // Statements. - // - os << "static const char persist_statement[];"; - - if (id != 0) - { - os << "static const char find_statement[];"; - - if (cc.total != cc.id + cc.inverse + cc.readonly) - os << "static const char update_statement[];"; - - os << "static const char erase_statement[];"; - - if (optimistic != 0) - os << "static const char optimistic_erase_statement[];"; - } - - if (options.generate_query ()) - { - os << "static const char query_statement[];" - << "static const char erase_query_statement[];" - << endl - << "static const char table_name[];"; - } - - os << endl; - - // - // 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 - << "persist (database&, " << (auto_id ? "" : "const ") << - "object_type&);" - << endl; - - if (id != 0) - { - // find () - // - if (c.default_ctor ()) - os << "static pointer_type" << endl - << "find (database&, const id_type&);" - << endl; - - os << "static bool" << endl - << "find (database&, const id_type&, object_type&);" - << endl; - - os << "static bool" << endl - << "reload (database&, object_type&);" - << endl; - - // update () - // - if (!readonly (c)) - os << "static void" << endl - << "update (database&, const object_type&);" - << endl; - - // erase () - // - os << "static void" << endl - << "erase (database&, const id_type&);" - << endl; - - os << "static void" << endl - << "erase (database&, const object_type&);" - << endl; - } - - // query () - // - if (options.generate_query ()) - { - os << "static result" << endl - << "query (database&, const query_base_type&);" - << endl; - - os << "static unsigned long long" << endl - << "erase_query (database&, const query_base_type&);" - << endl; - } - - // create_schema () - // - if (embedded_schema || separate_schema) - { - os << "static bool" << endl - << "create_schema (database&, unsigned short pass, bool drop);" - << endl; - } - - object_public_extra_post (c); - - // Implementation details. - // - os << "public:" << endl; - - if (id != 0) - { - // Load the object image. - // - os << "static bool" << endl - << "find_ (" << db << "::object_statements< object_type >&, " << - "const id_type&);" - << endl; - - // Load the rest of the object (containers, etc). Expects the id - // image in the object statements to be initialized to the object - // id. - // - os << "static void" << endl - << "load_ (" << db << "::object_statements< object_type >&, " << - "object_type&);" - << endl; - } - - os << "};"; - } + traverse_object (type& c); virtual void view_public_extra_pre (type&) @@ -1393,100 +1044,17 @@ namespace relational os << "typedef " << db << "::query query_base_type;" << endl; - // query_type + // query_type (definition generated by class2). // size_t obj_count (c.get ("object-count")); if (obj_count != 0) { - view_objects& objs (c.get ("objects")); - if (obj_count > 1) - { - os << "struct query_columns" - << "{"; - - for (view_objects::const_iterator i (objs.begin ()); - i < objs.end (); - ++i) - { - if (i->kind != view_object::object) - continue; // Skip tables. + os << "struct query_columns;"; - bool alias (!i->alias.empty ()); - semantics::class_& o (*i->obj); - string const& name (alias ? i->alias : class_name (o)); - string const& type (class_fq_name (o)); - qname const& table (table_name (o)); - - os << "// " << name << endl - << "//" << endl; - - if (alias && (table.qualified () || i->alias != table.uname ())) - os << "static const char " << name << "_alias_[];" - << endl - << "typedef" << endl - << "odb::pointer_query_columns< " << type << ", " << - name << "_alias_ >" << endl - << name << ";" - << endl; - else - os << "typedef" << endl - << "odb::pointer_query_columns<" << endl - << " " << type << "," << endl - << " " << "odb::access::object_traits< " << type << - " >::table_name >" << endl - << name << ";" - << endl; - } - - os << "};" - << "struct query_type: query_base_type, query_columns" - << "{"; - } - else - { - // For a single object view we generate a shortcut without - // an intermediate typedef. - // - view_object const* vo (0); - for (view_objects::const_iterator i (objs.begin ()); - vo == 0 && i < objs.end (); - ++i) - { - if (i->kind == view_object::object) - vo = &*i; - } - - bool alias (!vo->alias.empty ()); - semantics::class_& o (*vo->obj); - string const& type (class_fq_name (o)); - qname const& table (table_name (o)); - - if (alias && (table.qualified () || vo->alias != table.uname ())) - os << "static const char query_alias[];" - << endl - << "struct query_type:" << endl - << " query_base_type," << endl - << " odb::pointer_query_columns< " << type << - ", query_alias >" - << "{"; - else - os << "struct query_type:" << endl - << " query_base_type," << endl - << " odb::pointer_query_columns<" << endl - << " " << type << "," << endl - << " odb::access::object_traits< " << type << - " >::table_name >" - << "{"; - } - - os << "query_type ();" - << "query_type (bool);" - << "query_type (const char*);" - << "query_type (const std::string&);" - << "query_type (const query_base_type&);" - << "};"; + os << "struct query_type;" + << endl; } else os << "typedef query_base_type query_type;" @@ -1626,6 +1194,7 @@ namespace relational instance image_type_; instance id_image_member_; instance version_image_member_; + instance discriminator_image_member_; instance query_columns_type_; instance pointer_query_columns_type_; @@ -1663,14 +1232,15 @@ namespace relational virtual void traverse_object (type& c) { - bool abst (abstract (c)); + bool reuse_abst (abstract (c) && !polymorphic (c)); + string const& type (class_fq_name (c)); if (options.generate_query ()) { - bool has_ptr (has_a (c, test_pointer)); + bool has_ptr (has_a (c, test_pointer | include_base)); - if (has_ptr || !abst) + if (has_ptr || !reuse_abst) os << "// " << class_name (c) << endl << "//" << endl; @@ -1680,15 +1250,22 @@ namespace relational // in pass 1 (see the comment in class1 for details). // if (has_ptr) + { + instance t (alias_tags_, alias_specs_); + t->traverse (c); + query_columns_type_->traverse (c); + } // query_type // - if (!abst) + if (!reuse_abst) os << "struct access::object_traits< " << type << " >::" << "query_type:" << endl << " query_base_type," << endl - << " query_columns< " << type << ", table_name >" + << " query_columns<" << endl + << " " << type << "," << endl + << " access::object_traits< " << type << "> >" << "{" << "query_type ();" << "query_type (bool);" @@ -1702,8 +1279,141 @@ namespace relational } virtual void - traverse_view (type&) + traverse_view (type& c) { + string const& type (class_fq_name (c)); + + // query_type + // + size_t obj_count (c.get ("object-count")); + + if (obj_count != 0) + { + os << "// " << class_name (c) << endl + << "//" << endl; + + view_objects& objs (c.get ("objects")); + + // Generate the alias tags and alias_traits specializations. + // + { + instance at (alias_tags_, alias_specs_); + + for (view_objects::const_iterator i (objs.begin ()); + i < objs.end (); + ++i) + { + if (i->kind != view_object::object) + continue; // Skip tables. + + if (i->alias.empty ()) + continue; + + semantics::class_& o (*i->obj); + qname const& t (table_name (o)); + + // Check that the alias is not the same as the table name + // (if this is a polymorphic object, then the alias is just + // a prefix). + // + if (polymorphic (o) || t.qualified () || i->alias != t.uname ()) + at->generate (i->alias, o); + } + } + + if (obj_count > 1) + { + os << "struct access::view_traits< " << type << " >::query_columns" + << "{"; + + for (view_objects::const_iterator i (objs.begin ()); + i < objs.end (); + ++i) + { + if (i->kind != view_object::object) + continue; // Skip tables. + + bool alias (!i->alias.empty ()); + semantics::class_& o (*i->obj); + string const& oname (alias ? i->alias : class_name (o)); + string const& otype (class_fq_name (o)); + qname const& table (table_name (o)); + + os << "// " << oname << endl + << "//" << endl + << "typedef" << endl + << "odb::pointer_query_columns<" << endl + << " " << otype << "," << endl; + + if (alias && (polymorphic (o) || + table.qualified () || + i->alias != table.uname ())) + { + string tag (escape (i->alias + "_alias_tag")); + os << " odb::alias_traits< " << otype << ", " << + tag << " > >" << endl; + } + else + os << " odb::access::object_traits< " << otype << + " > >" << endl; + + os << oname << ";" + << endl; + } + + os << "};"; + + os << "struct access::view_traits< " << type << " >::" << + "query_type:" << endl + << " query_base_type, query_columns"; + } + else + { + // For a single object view we generate a shortcut without + // an intermediate typedef. + // + view_object const* vo (0); + for (view_objects::const_iterator i (objs.begin ()); + vo == 0 && i < objs.end (); + ++i) + { + if (i->kind == view_object::object) + vo = &*i; + } + + bool alias (!vo->alias.empty ()); + semantics::class_& o (*vo->obj); + string const& otype (class_fq_name (o)); + qname const& table (table_name (o)); + + os << "struct access::view_traits< " << type << " >::" << + "query_type:" << endl + << " query_base_type," << endl + << " odb::pointer_query_columns<" << endl + << " " << otype << "," << endl; + + if (alias && (polymorphic (o) || + table.qualified () || + vo->alias != table.uname ())) + { + string tag (escape (vo->alias + "_alias_tag")); + os << " odb::alias_traits< " << otype << ", " << + tag << " > >"; + } + else + os << " odb::access::object_traits< " << otype << " > >"; + } + + os << "{" + << "query_type ();" + << "query_type (bool);" + << "query_type (const char*);" + << "query_type (const std::string&);" + << "query_type (const query_base_type&);" + << "};"; + } + + // Move header comment out of if-block if adding any code here. } virtual void @@ -1713,6 +1423,9 @@ namespace relational private: instance query_columns_type_; + + std::set alias_tags_; + std::set alias_specs_; }; struct include: virtual context @@ -1728,6 +1441,7 @@ namespace relational os << "#include " << endl << "#include " << endl + << "#include " << endl << "#include " << endl; if (options.generate_query ()) diff --git a/odb/relational/inline.cxx b/odb/relational/inline.cxx index d1626d8..ca37e9d 100644 --- a/odb/relational/inline.cxx +++ b/odb/relational/inline.cxx @@ -17,6 +17,9 @@ namespace relational context ctx; ostream& os (ctx.os); + instance i; + i->generate (); + traversal::unit unit; traversal::defines unit_defines; typedefs unit_typedefs (false); diff --git a/odb/relational/inline.hxx b/odb/relational/inline.hxx index 5691d3e..0c585b8 100644 --- a/odb/relational/inline.hxx +++ b/odb/relational/inline.hxx @@ -102,10 +102,6 @@ namespace relational virtual void traverse_object (type& c) { - bool abstract (context::abstract (c)); - string const& type (class_fq_name (c)); - string traits ("access::object_traits< " + type + " >"); - semantics::data_member* id (id_member (c)); bool base_id (id ? &id->scope () != &c : false); // Comes from base. @@ -114,10 +110,18 @@ namespace relational // Base class the contains the object id and version for optimistic // concurrency. // - semantics::class_* base ( - id != 0 && base_id - ? dynamic_cast (&id->scope ()) - : 0); + type* base ( + id != 0 && base_id ? dynamic_cast (&id->scope ()) : 0); + + type* poly_root (context::polymorphic (c)); + bool poly (poly_root != 0); + bool poly_derived (poly && poly_root != &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 @@ -127,7 +131,7 @@ namespace relational // id (object_type) // - if (id != 0 || !abstract) + if (id != 0 || !reuse_abst) { os << "inline" << endl << traits << "::id_type" << endl @@ -147,77 +151,128 @@ namespace relational os << "}"; } - if (id != 0) + if (id != 0 && base_id) { - // id (image_type) - // - if (options.generate_query () && base_id) + if (!poly_derived) { - os << "inline" << endl - << traits << "::id_type" << endl - << traits << "::" << endl - << "id (const image_type& i)" - << "{" - << "return object_traits< " << class_fq_name (*base) << - " >::id (i);" - << "}"; + // id (image_type) + // + if (options.generate_query ()) + { + os << "inline" << endl + << traits << "::id_type" << endl + << traits << "::" << endl + << "id (const image_type& i)" + << "{" + << "return object_traits< " << class_fq_name (*base) << + " >::id (i);" + << "}"; + } + + // version (image_type) + // + if (optimistic != 0) + { + os << "inline" << endl + << traits << "::version_type" << endl + << traits << "::" << endl + << "version (const image_type& i)" + << "{" + << "return object_traits< " << class_fq_name (*base) << + " >::version (i);" + << "}"; + } } - // version (image_type) + // bind (id_image_type) // - if (optimistic != 0 && base_id) - { - os << "inline" << endl - << traits << "::version_type" << endl - << traits << "::" << endl - << "version (const image_type& i)" - << "{" - << "return object_traits< " << class_fq_name (*base) << - " >::version (i);" - << "}"; - } + os << "inline" << endl + << "void " << traits << "::" << endl + << "bind (" << bind_vector << " b, id_image_type& i" << + (optimistic != 0 ? ", bool bv" : "") << ")" + << "{" + << "object_traits< " << class_fq_name (*base) << + " >::bind (b, i" << (optimistic != 0 ? ", bv" : "") << ");" + << "}"; - // bind (id_image_type) + os << "inline" << endl + << "void " << traits << "::" << endl + << "init (id_image_type& i, const id_type& id" << + (optimistic != 0 ? ", const version_type* v" : "") << ")" + << "{" + << "object_traits< " << class_fq_name (*base) << + " >::init (i, id" << (optimistic != 0 ? ", v" : "") << ");" + << "}"; + } + + if (poly_derived) + { + size_t depth (polymorphic_depth (c)); + + // check_version // - if (base_id) + os << "inline" << endl + << "bool " << traits << "::" << endl + << "check_version (const std::size_t* v, const image_type& i)" + << "{" + << "return "; + + string image ("i."); + for (size_t i (0); i < depth; ++i) { - os << "inline" << endl - << "void " << traits << "::" << endl - << "bind (" << bind_vector << " b, id_image_type& i)" - << "{" - << "object_traits< " << class_fq_name (*base) << - " >::bind (b, i);" - << "}"; + os << (i == 0 ? "" : " ||") << endl + << " v[" << i << "UL] != " << image << "version"; + + image += "base->"; } - if (base_id) + os << ";" + << "}"; + + // update_version + // + os << "inline" << endl + << "void " << traits << "::" << endl + << "update_version (std::size_t* v, const image_type& i, " << + db << "::binding* b)" + << "{"; + + image = "i."; + for (size_t i (0); i < depth; ++i) { - os << "inline" << endl - << "void " << traits << "::" << endl - << "init (id_image_type& i, const id_type& id" << - (optimistic != 0 ? ", const version_type* v" : "") << ")" - << "{" - << "object_traits< " << class_fq_name (*base) << - " >::init (i, id" << (optimistic != 0 ? ", v" : "") << ");" - << "}"; + os << "v[" << i << "UL] = " << image << "version;"; + image += "base->"; } + + // A poly-abstract class always has only one entry in the + // bindings array. + // + if (abst) + os << "b[0].version++;"; + else + for (size_t i (0); i < depth; ++i) + os << "b[" << i << "UL].version++;"; + + os << "}"; } // - // The rest only applies to concrete objects. + // The rest does not apply to reuse-abstract objects. // - if (abstract) + if (reuse_abst) return; // erase (object_type) // - if (id != 0 && optimistic == 0) + if (id != 0 && !poly && optimistic == 0) { os << "inline" << endl << "void " << traits << "::" << endl << "erase (database& db, const object_type& obj)" << "{" + << "callback (db, obj, callback_event::pre_erase);" << "erase (db, id (obj));" + << "callback (db, obj, callback_event::post_erase);" << "}"; } @@ -287,15 +342,92 @@ namespace relational // load_() // - if (id != 0 && !has_a (c, test_container)) + if (id != 0 && !(poly_derived || has_a (c, test_container))) { os << "inline" << endl << "void " << traits << "::" << endl - << "load_ (" << db << "::object_statements< object_type >&, " << - "object_type&)" + << "load_ ("; + + if (poly && !poly_derived) + os << "base_statements_type&, "; + else + os << "statements_type&, "; + + os << "object_type&)" << "{" << "}"; } + + if (poly && need_image_clone && options.generate_query ()) + { + // root_image () + // + os << "inline" << endl + << traits << "::root_traits::image_type&" << endl + << traits << "::" << endl + << "root_image (image_type& i)" + << "{"; + + if (poly_derived) + os << "return object_traits::root_image (*i.base);"; + else + os << "return i;"; + + os << "}"; + + // clone_image () + // + os << "inline" << endl + << traits << "::image_type*" << endl + << traits << "::" << endl + << "clone_image (const image_type& i)" + << "{"; + + if (poly_derived) + os << "typedef object_traits base_traits;" + << endl + << "details::unique_ptr p (" << endl + << "base_traits::clone_image (*i.base));" + << "image_type* c (new image_type (i));" + << "c->base = p.release ();" + << "return c;"; + else + os << "return new image_type (i);"; + + os << "}"; + + // copy_image () + // + os << "inline" << endl + << "void " << traits << "::" << endl + << "copy_image (image_type& d, const image_type& s)" + << "{"; + + if (poly_derived) + os << "typedef object_traits base_traits;" + << endl + << "base_traits::image_type* b (d.base);" + << "base_traits::copy_image (*b, *s.base);" + << "d = s;" // Overwrites the base pointer. + << "d.base = b;"; + else + os << "d = s;"; + + os << "}"; + + // free_image () + // + os << "inline" << endl + << "void " << traits << "::" << endl + << "free_image (image_type* i)" + << "{"; + + if (poly_derived) + os << "object_traits::free_image (i->base);"; + + os << "delete i;" + << "}"; + } } virtual void @@ -379,6 +511,19 @@ namespace relational private: instance callback_calls_; }; + + struct include: virtual context + { + typedef include base; + + virtual void + generate () + { + if (features.polymorphic_object && options.generate_query ()) + os << "#include " << endl + << endl; + } + }; } } diff --git a/odb/relational/model.cxx b/odb/relational/model.cxx index 235350d..bd91b3c 100644 --- a/odb/relational/model.cxx +++ b/odb/relational/model.cxx @@ -147,7 +147,7 @@ namespace relational semantics::node& d (*e.dup.get ("cxx-node")); cerr << d.file () << ":" << d.line () << ":" << d.column () - << ": error: " << e.dup.kind () << " name '" << e.orig_name + << ": error: " << e.dup.kind () << " name '" << e.name << "' conflicts with an already defined " << e.orig.kind () << " name" << endl; diff --git a/odb/relational/model.hxx b/odb/relational/model.hxx index 66db3d6..afaa90d 100644 --- a/odb/relational/model.hxx +++ b/odb/relational/model.hxx @@ -218,6 +218,7 @@ namespace relational traverse_pointer (semantics::data_member& m, semantics::class_& c) { using sema_rel::column; + using sema_rel::foreign_key; // Ignore inverse object pointers. // @@ -227,11 +228,13 @@ namespace relational string id (id_prefix_ + (key_prefix_.empty () ? m.name () : key_prefix_)); - sema_rel::foreign_key& fk ( - model_.new_node ( - id, - table_name (c), - true)); // deferred + bool deferred (m.get ("deferred", true)); + foreign_key::action on_delete ( + m.get ("on-delete", foreign_key::no_action)); + + foreign_key& fk ( + model_.new_node ( + id, table_name (c), deferred, on_delete)); fk.set ("cxx-node", static_cast (&m)); @@ -529,7 +532,10 @@ namespace relational if (class_file (c) != unit.file ()) return; - if (!object (c) || abstract (c)) + if (!object (c)) + return; + + if (abstract (c) && !polymorphic (c)) return; qname const& name (table_name (c)); diff --git a/odb/relational/mssql/context.cxx b/odb/relational/mssql/context.cxx index 1fddbe9..0b36366 100644 --- a/odb/relational/mssql/context.cxx +++ b/odb/relational/mssql/context.cxx @@ -73,8 +73,9 @@ namespace relational context (ostream& os, semantics::unit& u, options_type const& ops, + features_type& f, sema_rel::model* m) - : root_context (os, u, ops, data_ptr (new (shared) data (os))), + : root_context (os, u, ops, f, data_ptr (new (shared) data (os))), base_context (static_cast (root_context::data_.get ()), m), data_ (static_cast (base_context::data_)) { @@ -85,6 +86,7 @@ namespace relational need_alias_as = true; insert_send_auto_id = false; delay_freeing_statement_result = true; + need_image_clone = true; data_->bind_vector_ = "mssql::bind*"; // Populate the C++ type to DB type map. diff --git a/odb/relational/mssql/context.hxx b/odb/relational/mssql/context.hxx index fe30746..b0cba1e 100644 --- a/odb/relational/mssql/context.hxx +++ b/odb/relational/mssql/context.hxx @@ -118,6 +118,7 @@ namespace relational context (std::ostream&, semantics::unit&, options_type const&, + features_type&, sema_rel::model*); static context& diff --git a/odb/relational/mssql/header.cxx b/odb/relational/mssql/header.cxx index 08ef306..957f880 100644 --- a/odb/relational/mssql/header.cxx +++ b/odb/relational/mssql/header.cxx @@ -22,24 +22,31 @@ namespace relational virtual void image_extra (type& c) { - if (!(composite (c) || abstract (c))) + if (!(composite (c) || (abstract (c) && !polymorphic (c)))) { - bool gc (options.generate_query ()); + type* poly_root (polymorphic (c)); - if (gc) - os << "mssql::change_callback change_callback_;" - << endl; + // If this is a polymorphic type, only add callback to the root. + // + if (poly_root == 0 || poly_root == &c) + { + bool gc (options.generate_query ()); - os << "mssql::change_callback*" << endl - << "change_callback ()" - << "{"; + if (gc) + os << "mssql::change_callback change_callback_;" + << endl; - if (gc) - os << "return &change_callback_;"; - else - os << "return 0;"; + os << "mssql::change_callback*" << endl + << "change_callback ()" + << "{"; - os << "}"; + if (gc) + os << "return &change_callback_;"; + else + os << "return 0;"; + + os << "}"; + } } } }; diff --git a/odb/relational/mssql/source.cxx b/odb/relational/mssql/source.cxx index 2d8d41d..af43d12 100644 --- a/odb/relational/mssql/source.cxx +++ b/odb/relational/mssql/source.cxx @@ -2,8 +2,6 @@ // copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file -#include - #include #include @@ -69,134 +67,17 @@ namespace relational "mssql::bind::bigint" }; - struct bind_member: relational::bind_member, member_base + struct bind_member: relational::bind_member_impl, + member_base { bind_member (base const& x) - : member_base::base (x), // virtual base - base (x), + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), member_base (x) { } - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - ostringstream ostr; - ostr << "b[n]"; - b = ostr.str (); - - arg = arg_override_.empty () ? string ("i") : arg_override_; - - if (var_override_.empty ()) - { - os << "// " << mi.m.name () << endl - << "//" << endl; - - if (id (mi.m) && auto_ (mi.m)) - // For SQL Server we don't send auto id in INSERT. - // - os << "if (sk != statement_insert && sk != statement_update)" - << "{"; - else if (inverse (mi.m, key_prefix_) || version (mi.m)) - os << "if (sk == statement_select)" - << "{"; - // If the whole class is readonly, then we will never be - // called with sk == statement_update. - // - else if (!readonly (*context::top_object)) - { - semantics::class_* c; - - if (id (mi.m) || - readonly (mi.m) || - ((c = composite (mi.t)) && readonly (*c))) - os << "if (sk != statement_update)" - << "{"; - } - } - - return true; - } - - virtual void - post (member_info& mi) - { - if (var_override_.empty ()) - { - semantics::class_* c; - - if ((c = composite (mi.t))) - { - bool ro (readonly (*c)); - column_count_type const& cc (column_count (*c)); - - os << "n += " << cc.total << "UL"; - - // select = total - // insert = total - inverse - // update = total - inverse - readonly - // - if (cc.inverse != 0 || (!ro && cc.readonly != 0)) - { - os << " - (" << endl - << "sk == statement_select ? 0 : "; - - if (cc.inverse != 0) - os << cc.inverse << "UL"; - - if (!ro && cc.readonly != 0) - { - if (cc.inverse != 0) - os << " + "; - - os << "(" << endl - << "sk == statement_insert ? 0 : " << - cc.readonly << "UL)"; - } - - os << ")"; - } - - os << ";"; - } - else - os << "n++;"; - - bool block (false); - - // The same logic as in pre(). - // - if (id (mi.m) && auto_ (mi.m)) - block = true; - else if (inverse (mi.m, key_prefix_) || version (mi.m)) - block = true; - else if (!readonly (*context::top_object)) - { - semantics::class_* c; - - if (id (mi.m) || - readonly (mi.m) || - ((c = composite (mi.t)) && readonly (*c))) - block = true; - } - - if (block) - os << "}"; - else - os << endl; - } - } - - virtual void - traverse_composite (member_info& mi) - { - os << "composite_value_traits< " << mi.fq_type () << - " >::bind (b + n, " << arg << "." << mi.var << "value, sk);"; - } - virtual void traverse_integer (member_info& mi) { @@ -401,10 +282,6 @@ namespace relational << b << ".buffer = &" << arg << "." << mi.var << "value;" << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"; } - - private: - string b; - string arg; }; entry bind_member_; @@ -958,16 +835,34 @@ namespace relational virtual void init_image_pre (type& c) { - if (options.generate_query () && !(composite (c) || abstract (c))) - os << "if (i.change_callback_.callback != 0)" << endl - << "(i.change_callback_.callback) (i.change_callback_.context);" - << endl; + if (options.generate_query () && + !(composite (c) || (abstract (c) && !polymorphic (c)))) + { + type* poly_root (polymorphic (c)); + bool poly_derived (poly_root != 0 && poly_root != &c); + + if (poly_derived) + os << "{" + << "root_traits::image_type& ri (root_image (i));" + << endl; + + string i (poly_derived ? "ri" : "i"); + + os << "if (" << i << ".change_callback_.callback != 0)" << endl + << "(" << i << ".change_callback_.callback) (" << + i << ".change_callback_.context);"; + + if (poly_derived) + os << "}"; + else + os << endl; + } } virtual void init_value_extra () { - os << "sts.find_statement ().stream_result ();"; + os << "st.stream_result ();"; } virtual void @@ -980,6 +875,13 @@ namespace relational if (id == 0 || !auto_ (*id)) return; + // If we are a derived type in a polymorphic hierarchy, then + // auto id is handled by the root. + // + if (type* root = polymorphic (c)) + if (root != &c) + return; + // SQL Server 2005 has a bug that causes it to fail on an // INSERT statement with the OUTPUT clause if data for one // of the inserted columns is supplied at execution (long diff --git a/odb/relational/mysql/context.cxx b/odb/relational/mysql/context.cxx index 5d45934..ec1a967 100644 --- a/odb/relational/mysql/context.cxx +++ b/odb/relational/mysql/context.cxx @@ -69,8 +69,9 @@ namespace relational context (ostream& os, semantics::unit& u, options_type const& ops, + features_type& f, sema_rel::model* m) - : root_context (os, u, ops, data_ptr (new (shared) data (os))), + : root_context (os, u, ops, f, data_ptr (new (shared) data (os))), base_context (static_cast (root_context::data_.get ()), m), data_ (static_cast (base_context::data_)) { @@ -81,6 +82,7 @@ namespace relational need_alias_as = true; insert_send_auto_id = true; delay_freeing_statement_result = false; + need_image_clone = false; data_->bind_vector_ = "MYSQL_BIND*"; data_->truncated_vector_ = "my_bool*"; diff --git a/odb/relational/mysql/context.hxx b/odb/relational/mysql/context.hxx index 294b5d1..8759987 100644 --- a/odb/relational/mysql/context.hxx +++ b/odb/relational/mysql/context.hxx @@ -123,6 +123,7 @@ namespace relational context (std::ostream&, semantics::unit&, options_type const&, + features_type&, sema_rel::model*); static context& diff --git a/odb/relational/mysql/source.cxx b/odb/relational/mysql/source.cxx index 3a784af..8591aca 100644 --- a/odb/relational/mysql/source.cxx +++ b/odb/relational/mysql/source.cxx @@ -142,7 +142,7 @@ namespace relational r += ")"; sc_.push_back ( - relational::statement_column (r, type, m, key_prefix_)); + relational::statement_column (table, r, type, m, key_prefix_)); } }; entry object_columns_; @@ -152,7 +152,9 @@ namespace relational view_columns (base const& x): base (x) {} virtual void - column (semantics::data_member& m, string const& column) + column (semantics::data_member& m, + string const& table, + string const& column) { // The same idea as in object_columns. // @@ -160,7 +162,7 @@ namespace relational if (parse_sql_type (type, m).type != sql_type::ENUM) { - base::column (m, column); + base::column (m, table, column); return; } @@ -172,7 +174,7 @@ namespace relational r += column; r += ")"; - sc_.push_back (relational::statement_column (r, type, m)); + sc_.push_back (relational::statement_column (table, r, type, m)); } }; entry view_columns_; @@ -181,127 +183,17 @@ namespace relational // bind // - struct bind_member: relational::bind_member, member_base + struct bind_member: relational::bind_member_impl, + member_base { bind_member (base const& x) - : member_base::base (x), // virtual base - base (x), + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), member_base (x) { } - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - ostringstream ostr; - ostr << "b[n]"; - b = ostr.str (); - - arg = arg_override_.empty () ? string ("i") : arg_override_; - - if (var_override_.empty ()) - { - os << "// " << mi.m.name () << endl - << "//" << endl; - - if (inverse (mi.m, key_prefix_) || version (mi.m)) - os << "if (sk == statement_select)" - << "{"; - // If the whole class is readonly, then we will never be - // called with sk == statement_update. - // - else if (!readonly (*context::top_object)) - { - semantics::class_* c; - - if (id (mi.m) || - readonly (mi.m) || - ((c = composite (mi.t)) && readonly (*c))) - os << "if (sk != statement_update)" - << "{"; - } - } - - return true; - } - - virtual void - post (member_info& mi) - { - if (var_override_.empty ()) - { - semantics::class_* c; - - if ((c = composite (mi.t))) - { - bool ro (readonly (*c)); - column_count_type const& cc (column_count (*c)); - - os << "n += " << cc.total << "UL"; - - // select = total - // insert = total - inverse - // update = total - inverse - readonly - // - if (cc.inverse != 0 || (!ro && cc.readonly != 0)) - { - os << " - (" << endl - << "sk == statement_select ? 0 : "; - - if (cc.inverse != 0) - os << cc.inverse << "UL"; - - if (!ro && cc.readonly != 0) - { - if (cc.inverse != 0) - os << " + "; - - os << "(" << endl - << "sk == statement_insert ? 0 : " << - cc.readonly << "UL)"; - } - - os << ")"; - } - - os << ";"; - } - else - os << "n++;"; - - bool block (false); - - // The same logic as in pre(). - // - if (inverse (mi.m, key_prefix_) || version (mi.m)) - block = true; - else if (!readonly (*context::top_object)) - { - semantics::class_* c; - - if (id (mi.m) || - readonly (mi.m) || - ((c = composite (mi.t)) && readonly (*c))) - block = true; - } - - if (block) - os << "}"; - else - os << endl; - } - } - - virtual void - traverse_composite (member_info& mi) - { - os << "composite_value_traits< " << mi.fq_type () << - " >::bind (b + n, " << arg << "." << mi.var << "value, sk);"; - } - virtual void traverse_integer (member_info& mi) { @@ -415,10 +307,6 @@ namespace relational << b << ".length = &" << arg << "." << mi.var << "size;" << b << ".is_null = &" << arg << "." << mi.var << "null;"; } - - private: - string b; - string arg; }; entry bind_member_; diff --git a/odb/relational/oracle/context.cxx b/odb/relational/oracle/context.cxx index 2fa566c..a44e3ee 100644 --- a/odb/relational/oracle/context.cxx +++ b/odb/relational/oracle/context.cxx @@ -68,8 +68,9 @@ namespace relational context (ostream& os, semantics::unit& u, options_type const& ops, + features_type& f, sema_rel::model* m) - : root_context (os, u, ops, data_ptr (new (shared) data (os))), + : root_context (os, u, ops, f, data_ptr (new (shared) data (os))), base_context (static_cast (root_context::data_.get ()), m), data_ (static_cast (base_context::data_)) { @@ -80,6 +81,7 @@ namespace relational need_alias_as = false; insert_send_auto_id = true; delay_freeing_statement_result = false; + need_image_clone = true; data_->bind_vector_ = "oracle::bind*"; // Populate the C++ type to DB type map. diff --git a/odb/relational/oracle/context.hxx b/odb/relational/oracle/context.hxx index 37716f0..6b7e968 100644 --- a/odb/relational/oracle/context.hxx +++ b/odb/relational/oracle/context.hxx @@ -112,6 +112,7 @@ namespace relational context (std::ostream&, semantics::unit&, options_type const&, + features_type&, sema_rel::model*); static context& diff --git a/odb/relational/oracle/header.cxx b/odb/relational/oracle/header.cxx index 91f1d93..3eca2e2 100644 --- a/odb/relational/oracle/header.cxx +++ b/odb/relational/oracle/header.cxx @@ -22,24 +22,31 @@ namespace relational virtual void image_extra (type& c) { - if (!(composite (c) || abstract (c))) + if (!(composite (c) || (abstract (c) && !polymorphic (c)))) { - bool gc (options.generate_query ()); + type* poly_root (polymorphic (c)); - if (gc) - os << "oracle::change_callback change_callback_;" - << endl; + // If this is a polymorphic type, only add callback to the root. + // + if (poly_root == 0 || poly_root == &c) + { + bool gc (options.generate_query ()); - os << "oracle::change_callback*" << endl - << "change_callback ()" - << "{"; + if (gc) + os << "oracle::change_callback change_callback_;" + << endl; - if (gc) - os << "return &change_callback_;"; - else - os << "return 0;"; + os << "oracle::change_callback*" << endl + << "change_callback ()" + << "{"; - os << "}"; + if (gc) + os << "return &change_callback_;"; + else + os << "return 0;"; + + os << "}"; + } } } }; diff --git a/odb/relational/oracle/source.cxx b/odb/relational/oracle/source.cxx index 1d7d81f..4ce6e2a 100644 --- a/odb/relational/oracle/source.cxx +++ b/odb/relational/oracle/source.cxx @@ -2,8 +2,6 @@ // copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file -#include - #include #include @@ -63,127 +61,17 @@ namespace relational // bind // - struct bind_member: relational::bind_member, member_base + struct bind_member: relational::bind_member_impl, + member_base { bind_member (base const& x) - : member_base::base (x), // virtual base - base (x), + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), member_base (x) { } - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - ostringstream ostr; - ostr << "b[n]"; - b = ostr.str (); - - arg = arg_override_.empty () ? string ("i") : arg_override_; - - if (var_override_.empty ()) - { - os << "// " << mi.m.name () << endl - << "//" << endl; - - if (inverse (mi.m, key_prefix_) || version (mi.m)) - os << "if (sk == statement_select)" - << "{"; - // If the whole class is readonly, then we will never be - // called with sk == statement_update. - // - else if (!readonly (*context::top_object)) - { - semantics::class_* c; - - if (id (mi.m) || - readonly (mi.m) || - ((c = composite (mi.t)) && readonly (*c))) - os << "if (sk != statement_update)" - << "{"; - } - } - - return true; - } - - virtual void - post (member_info& mi) - { - if (var_override_.empty ()) - { - semantics::class_* c; - - if ((c = composite (mi.t))) - { - bool ro (readonly (*c)); - column_count_type const& cc (column_count (*c)); - - os << "n += " << cc.total << "UL"; - - // select = total - // insert = total - inverse - // update = total - inverse - readonly - // - if (cc.inverse != 0 || (!ro && cc.readonly != 0)) - { - os << " - (" << endl - << "sk == statement_select ? 0 : "; - - if (cc.inverse != 0) - os << cc.inverse << "UL"; - - if (!ro && cc.readonly != 0) - { - if (cc.inverse != 0) - os << " + "; - - os << "(" << endl - << "sk == statement_insert ? 0 : " << - cc.readonly << "UL)"; - } - - os << ")"; - } - - os << ";"; - } - else - os << "n++;"; - - bool block (false); - - // The same logic as in pre(). - // - if (inverse (mi.m, key_prefix_) || version (mi.m)) - block = true; - else if (!readonly (*context::top_object)) - { - semantics::class_* c; - - if (id (mi.m) || - readonly (mi.m) || - ((c = composite (mi.t)) && readonly (*c))) - block = true; - } - - if (block) - os << "}"; - else - os << endl; - } - } - - virtual void - traverse_composite (member_info& mi) - { - os << "composite_value_traits< " << mi.fq_type () << - " >::bind (b + n, " << arg << "." << mi.var << "value, sk);"; - } - virtual void traverse_int32 (member_info& mi) { @@ -305,10 +193,6 @@ namespace relational << b << ".callback = &" << arg << "." << mi.var << "callback;" << endl; } - - private: - string b; - string arg; }; entry bind_member_; @@ -632,16 +516,34 @@ namespace relational virtual void init_image_pre (type& c) { - if (options.generate_query () && !(composite (c) || abstract (c))) - os << "if (i.change_callback_.callback != 0)" << endl - << "(i.change_callback_.callback) (i.change_callback_.context);" - << endl; + if (options.generate_query () && + !(composite (c) || (abstract (c) && !polymorphic (c)))) + { + type* poly_root (polymorphic (c)); + bool poly_derived (poly_root != 0 && poly_root != &c); + + if (poly_derived) + os << "{" + << "root_traits::image_type& ri (root_image (i));" + << endl; + + string i (poly_derived ? "ri" : "i"); + + os << "if (" << i << ".change_callback_.callback != 0)" << endl + << "(" << i << ".change_callback_.callback) (" << + i << ".change_callback_.context, 0);"; + + if (poly_derived) + os << "}"; + else + os << endl; + } } virtual void init_value_extra () { - os << "sts.find_statement ().stream_result ();"; + os << "st.stream_result ();"; } virtual void diff --git a/odb/relational/pgsql/context.cxx b/odb/relational/pgsql/context.cxx index 9ce333e..ca2b3a5 100644 --- a/odb/relational/pgsql/context.cxx +++ b/odb/relational/pgsql/context.cxx @@ -69,8 +69,9 @@ namespace relational context (ostream& os, semantics::unit& u, options_type const& ops, + features_type& f, sema_rel::model* m) - : root_context (os, u, ops, data_ptr (new (shared) data (os))), + : root_context (os, u, ops, f, data_ptr (new (shared) data (os))), base_context (static_cast (root_context::data_.get ()), m), data_ (static_cast (base_context::data_)) { @@ -81,6 +82,7 @@ namespace relational need_alias_as = true; insert_send_auto_id = true; delay_freeing_statement_result = false; + need_image_clone = false; data_->bind_vector_ = "pgsql::bind*"; data_->truncated_vector_ = "bool*"; diff --git a/odb/relational/pgsql/context.hxx b/odb/relational/pgsql/context.hxx index 1da353a..c572b01 100644 --- a/odb/relational/pgsql/context.hxx +++ b/odb/relational/pgsql/context.hxx @@ -110,6 +110,7 @@ namespace relational context (std::ostream&, semantics::unit&, options_type const&, + features_type&, sema_rel::model*); static context& diff --git a/odb/relational/pgsql/header.cxx b/odb/relational/pgsql/header.cxx index 0d5a10a..5141c6c 100644 --- a/odb/relational/pgsql/header.cxx +++ b/odb/relational/pgsql/header.cxx @@ -22,7 +22,13 @@ namespace relational virtual void object_public_extra_post (type& c) { - if (abstract (c)) + bool abst (abstract (c)); + + type* poly_root (polymorphic (c)); + bool poly (poly_root != 0); + bool poly_derived (poly && poly_root != &c); + + if (abst && !poly) return; semantics::data_member* id (id_member (c)); @@ -36,7 +42,14 @@ namespace relational if (id != 0) { - os << "static const char find_statement_name[];"; + if (poly_derived) + os << "static const char* const find_statement_names[" << + (abst ? "1" : "depth") << "];"; + else + os << "static const char find_statement_name[];"; + + if (poly && !poly_derived) + os << "static const char find_discriminator_statement_name[];"; if (cc.total != cc.id + cc.inverse + cc.readonly) os << "static const char update_statement_name[];"; @@ -66,8 +79,6 @@ namespace relational if (cc.total != cc.id + cc.inverse + cc.readonly) os << "static const unsigned int update_statement_types[];"; - os << "static const unsigned int erase_statement_types[];"; - if (optimistic != 0) os << "static const unsigned int " << "optimistic_erase_statement_types[];"; @@ -94,7 +105,7 @@ namespace relational virtual void container_public_extra_pre (semantics::data_member&) { - if (!object (c_) || abstract (c_)) + if (!object (c_) || (abstract (c_) && !polymorphic (c_))) return; // Container statement names. diff --git a/odb/relational/pgsql/source.cxx b/odb/relational/pgsql/source.cxx index ae19e05..c868df2 100644 --- a/odb/relational/pgsql/source.cxx +++ b/odb/relational/pgsql/source.cxx @@ -152,127 +152,17 @@ namespace relational // bind // - struct bind_member: relational::bind_member, member_base + struct bind_member: relational::bind_member_impl, + member_base { bind_member (base const& x) - : member_base::base (x), // virtual base - base (x), + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), member_base (x) { } - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - ostringstream ostr; - ostr << "b[n]"; - b = ostr.str (); - - arg = arg_override_.empty () ? string ("i") : arg_override_; - - if (var_override_.empty ()) - { - os << "// " << mi.m.name () << endl - << "//" << endl; - - if (inverse (mi.m, key_prefix_) || version (mi.m)) - os << "if (sk == statement_select)" - << "{"; - // If the whole class is readonly, then we will never be - // called with sk == statement_update. - // - else if (!readonly (*context::top_object)) - { - semantics::class_* c; - - if (id (mi.m) || - readonly (mi.m) || - ((c = composite (mi.t)) && readonly (*c))) - os << "if (sk != statement_update)" - << "{"; - } - } - - return true; - } - - virtual void - post (member_info& mi) - { - if (var_override_.empty ()) - { - semantics::class_* c; - - if ((c = composite (mi.t))) - { - bool ro (readonly (*c)); - column_count_type const& cc (column_count (*c)); - - os << "n += " << cc.total << "UL"; - - // select = total - // insert = total - inverse - // update = total - inverse - readonly - // - if (cc.inverse != 0 || (!ro && cc.readonly != 0)) - { - os << " - (" << endl - << "sk == statement_select ? 0 : "; - - if (cc.inverse != 0) - os << cc.inverse << "UL"; - - if (!ro && cc.readonly != 0) - { - if (cc.inverse != 0) - os << " + "; - - os << "(" << endl - << "sk == statement_insert ? 0 : " << - cc.readonly << "UL)"; - } - - os << ")"; - } - - os << ";"; - } - else - os << "n++;"; - - bool block (false); - - // The same logic as in pre(). - // - if (inverse (mi.m, key_prefix_) || version (mi.m)) - block = true; - else if (!readonly (*context::top_object)) - { - semantics::class_* c; - - if (id (mi.m) || - readonly (mi.m) || - ((c = composite (mi.t)) && readonly (*c))) - block = true; - } - - if (block) - os << "}"; - else - os << endl; - } - } - - virtual void - traverse_composite (member_info& mi) - { - os << "composite_value_traits< " << mi.fq_type () << - " >::bind (b + n, " << arg << "." << mi.var << "value, sk);"; - } - virtual void traverse_integer (member_info& mi) { @@ -351,10 +241,6 @@ namespace relational << b << ".buffer = " << arg << "." << mi.var << "value;" << b << ".is_null = &" << arg << "." << mi.var << "null;"; } - - private: - string b; - string arg; }; entry bind_member_; @@ -735,7 +621,13 @@ namespace relational virtual void object_extra (type& c) { - if (abstract (c)) + bool abst (abstract (c)); + + type* poly_root (polymorphic (c)); + bool poly (poly_root != 0); + bool poly_derived (poly && poly_root != &c); + + if (abst && !poly) return; semantics::data_member* id (id_member (c)); @@ -743,33 +635,58 @@ namespace relational column_count_type const& cc (column_count (c)); string const& n (class_fq_name (c)); - string traits ("access::object_traits< " + n + " >::"); string const& fn (flat_name (n)); - string name_decl ("const char " + traits); + string traits ("access::object_traits< " + n + " >"); - os << name_decl << endl - << "persist_statement_name[] = " << strlit (fn + "_persist") << - ";" + os << "const char " << traits << "::" << endl + << "persist_statement_name[] = " << strlit (fn + "_persist") << ";" << endl; if (id != 0) { - os << name_decl << endl - << "find_statement_name[] = " << strlit (fn + "_find") << ";" - << endl; + if (poly_derived) + { + os << "const char* const " << traits << "::" << endl + << "find_statement_names[] =" + << "{"; + + for (size_t i (0), n (abst ? 1 : polymorphic_depth (c)); + i < n; + ++i) + { + if (i != 0) + os << "," << endl; + + ostringstream ostr; + ostr << fn << "_find_" << i; + os << strlit (ostr.str ()); + } + + os << "};"; + } + else + os << "const char " << traits << "::" << endl + << "find_statement_name[] = " << strlit (fn + "_find") << ";" + << endl; + + if (poly && !poly_derived) + os << "const char " << traits << "::" << endl + << "find_discriminator_statement_name[] = " << + strlit (fn + "_find_discriminator") << ";" + << endl; if (cc.total != cc.id + cc.inverse + cc.readonly) - os << name_decl << endl + os << "const char " << traits << "::" << endl << "update_statement_name[] = " << strlit (fn + "_update") << ";" << endl; - os << name_decl << endl + os << "const char " << traits << "::" << endl << "erase_statement_name[] = " << strlit (fn + "_erase") << ";" << endl; if (optimistic != 0) - os << name_decl << endl + os << "const char " << traits << "::" << endl << "optimistic_erase_statement_name[] = " << strlit (fn + "_optimistic_erase") << ";" << endl; @@ -779,10 +696,10 @@ namespace relational // if (options.generate_query ()) { - os << name_decl << endl + os << "const char " << traits << "::" << endl << "query_statement_name[] = " << strlit (fn + "_query") << ";" << endl - << name_decl << endl + << "const char " << traits << "::" << endl << "erase_query_statement_name[] = " << strlit (fn + "_erase_query") << ";" << endl; @@ -790,12 +707,11 @@ namespace relational // Statement types. // - string oid_decl ("const unsigned int " + traits); // persist_statement_types. // { - os << oid_decl << endl + os << "const unsigned int " << traits << "::" << endl << "persist_statement_types[] =" << "{"; @@ -809,7 +725,7 @@ namespace relational // if (id != 0) { - os << oid_decl << endl + os << "const unsigned int " << traits << "::" << endl << "find_statement_types[] =" << "{"; @@ -823,7 +739,7 @@ namespace relational // if (id != 0 && cc.total != cc.id + cc.inverse + cc.readonly) { - os << oid_decl << endl + os << "const unsigned int " << traits << "::" << endl << "update_statement_types[] =" << "{"; @@ -844,23 +760,9 @@ namespace relational os << "};"; } - // erase_statement_types. - // - if (id != 0) - { - os << oid_decl << endl - << "erase_statement_types[] =" - << "{"; - - instance st (statement_where); - st->traverse (*id); - - os << "};"; - } - if (id != 0 && optimistic != 0) { - os << oid_decl << endl + os << "const unsigned int " << traits << "::" << endl << "optimistic_erase_statement_types[] =" << "{"; @@ -876,11 +778,10 @@ namespace relational view_extra (type& c) { string const& n (class_fq_name (c)); - string traits ("access::view_traits< " + n + " >::"); string const& fn (flat_name (n)); - string name_decl ("const char " + traits); + string traits ("access::view_traits< " + n + " >"); - os << name_decl << endl + os << "const char " << traits << "::" << endl << "query_statement_name[] = " << strlit (fn + "_query") << ";" << endl; } @@ -935,7 +836,7 @@ namespace relational virtual void container_extra (semantics::data_member& m, semantics::type& t) { - if (!object (c_) || abstract (c_)) + if (!object (c_) || (abstract (c_) && !polymorphic (c_))) return; string scope (scope_ + "::" + flat_prefix_ + public_name (m) + @@ -943,29 +844,24 @@ namespace relational // Statment names. // - string stmt_decl ("const char " + scope + "::"); // Prefix top-object name to avoid conflicts with inherited // member statement names. // - string stmt_prefix (class_fq_name (*top_object) + m.fq_name ()); + string fn (flat_name (class_fq_name (*top_object) + m.fq_name ())); - os << stmt_decl << endl - << "select_all_name[] = " << - strlit (stmt_prefix + "_select_all") << ";" + os << "const char " << scope << "::" << endl + << "select_all_name[] = " << strlit (fn + "_select_all") << ";" << endl - << stmt_decl << endl - << "insert_one_name[] = " << - strlit (stmt_prefix + "_insert_one") << ";" + << "const char " << scope << "::" << endl + << "insert_one_name[] = " << strlit (fn + "_insert_one") << ";" << endl - << stmt_decl << endl - << "delete_all_name[] = " << - strlit (stmt_prefix + "_delete_all") << ";" + << "const char " << scope << "::" << endl + << "delete_all_name[] = " << strlit (fn + "_delete_all") << ";" << endl; // Statement types. // - string type_decl ("const unsigned int " + scope + "::"); semantics::data_member* inv_m (inverse (m, "value")); bool inv (inv_m != 0); @@ -976,7 +872,7 @@ namespace relational // select_all statement types. // { - os << type_decl << endl + os << "const unsigned int " << scope << "::" << endl << "select_all_types[] =" << "{"; @@ -1003,7 +899,7 @@ namespace relational // insert_one statement types. // { - os << type_decl << endl + os << "const unsigned int " << scope << "::" << endl << "insert_one_types[] =" << "{"; @@ -1048,7 +944,7 @@ namespace relational // delete_all statement types. // { - os << type_decl << endl + os << "const unsigned int " << scope << "::" << endl << "delete_all_types[] =" << "{"; diff --git a/odb/relational/processor.cxx b/odb/relational/processor.cxx index e354b0f..cdacfd4 100644 --- a/odb/relational/processor.cxx +++ b/odb/relational/processor.cxx @@ -247,7 +247,9 @@ namespace relational if (type.empty () && wt != 0) type = database_type (*wt, whint, false); - if (id (m)) + // Use id mapping for discriminators. + // + if (id (m) || discriminator (m)) type = id_type; } @@ -768,14 +770,8 @@ namespace relational while (DECL_TEMPLATE_INFO (decl)) decl = DECL_TI_TEMPLATE (decl); - if (!unit.count ("tr1-pointer-used")) - { - unit.set ("tr1-pointer-used", false); - unit.set ("boost-pointer-used", false); - } - - bool& tr1 (unit.get ("tr1-pointer-used")); - bool& boost (unit.get ("boost-pointer-used")); + bool& tr1 (features.tr1_pointer); + bool& boost (features.boost_pointer); string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER)); @@ -918,9 +914,9 @@ namespace relational throw operation_failed (); } - // Make sure the pointed-to class is not abstract. + // Make sure the pointed-to class is not reuse-abstract. // - if (context::abstract (*c)) + if (abstract (*c) && !polymorphic (*c)) { os << m.file () << ":" << m.line () << ":" << m.column () << ": " << "error: pointed-to class '" << class_fq_name (*c) << "' " @@ -935,7 +931,7 @@ namespace relational // Make sure the pointed-to class has object id. // - if (context::id_member (*c) == 0) + if (id_member (*c) == 0) { os << m.file () << ":" << m.line () << ":" << m.column () << ": " << "error: pointed-to class '" << class_fq_name (*c) << "' " @@ -1285,7 +1281,7 @@ namespace relational string name; tree decl (0); - semantics::class_* obj (0); + view_object* vo (0); // Check if this is an alias. // @@ -1295,8 +1291,7 @@ namespace relational if (j != amap_.end ()) { - i->table = j->first; - obj = j->second->obj; + vo = j->second; // Skip '::'. // @@ -1312,13 +1307,13 @@ namespace relational cpp_ttype ptt; // Not used. decl = lookup::resolve_scoped_name ( - t, tt, ptt, lex_, obj->tree_node (), name, false); + t, tt, ptt, lex_, vo->obj->tree_node (), name, false); } } // If it is not an alias, do the normal lookup. // - if (obj == 0) + if (vo == 0) { // Also get the object type. We need to do it so that // we can get the correct (derived) table name (the @@ -1342,8 +1337,7 @@ namespace relational throw operation_failed (); } - obj = j->second->obj; - i->table = table_name (*obj); + vo = j->second; } // Check that we have a data member. @@ -1358,6 +1352,40 @@ namespace relational data_member* m (dynamic_cast (unit.find (decl))); i->member_path.push_back (m); + // Figure out the table name/alias for this member. + // + using semantics::class_; + + if (class_* root = polymorphic (*vo->obj)) + { + // If the object is polymorphic, then figure out which of the + // bases this member comes from and use the corresponding + // table. + // + class_* c (&static_cast (m->scope ())); + + // If this member's class is not polymorphic (root uses reuse + // inheritance), then use the root table. + // + if (!polymorphic (*c)) + c = root; + + // In a polymorphic hierarchy we have several tables and the + // provided alias is used as a prefix together with the table + // name to form the actual alias. + // + qname const& t (table_name (*c)); + + if (vo->alias.empty ()) + i->table = t; + else + i->table = qname (vo->alias + "_" + t.uname ()); + } + else + i->table = vo->alias.empty () + ? table_name (*vo->obj) + : qname (vo->alias); + // Finally, resolve nested members if any. // for (; tt == CPP_DOT; tt = lex_.next (t)) @@ -1440,7 +1468,7 @@ namespace relational } } } - // This member has no column information. If we are generting our + // This member has no column information. If we are generating our // own query, try to find a member with the same (or similar) name // in one of the associated objects. // @@ -1508,9 +1536,39 @@ namespace relational column_expr_part& ep (e.back ()); ep.kind = column_expr_part::reference; - ep.table = am.vo->alias.empty () - ? table_name (*am.vo->obj) - : qname (am.vo->alias); + + + // If this object is polymorphic, then figure out which of the + // bases this member comes from and use the corresponding table. + // + using semantics::class_; + + if (class_* root = polymorphic (*am.vo->obj)) + { + class_* c (&static_cast (am.m->scope ())); + + // If this member's class is not polymorphic (root uses reuse + // inheritance), then use the root table. + // + if (!polymorphic (*c)) + c = root; + + // In a polymorphic hierarchy we have several tables and the + // provided alias is used as a prefix together with the table + // name to form the actual alias. + // + qname const& t (table_name (*c)); + + if (am.vo->alias.empty ()) + ep.table = t; + else + ep.table = qname (am.vo->alias + "_" + t.uname ()); + } + else + ep.table = am.vo->alias.empty () + ? table_name (*am.vo->obj) + : qname (am.vo->alias); + ep.member_path.push_back (am.m); src_m = am.m; @@ -1553,7 +1611,23 @@ namespace relational traverse (view_object& vo) { member_.vo_ = &vo; - traverse (*vo.obj); + + // First look for an exact match. + // + { + member_.exact_ = true; + member_.found_ = false; + traverse (*vo.obj); + } + + // If we didn't find an exact match, then look for a public + // name match. + // + if (!member_.found_) + { + member_.exact_ = false; + traverse (*vo.obj); + } } virtual void @@ -1563,7 +1637,12 @@ namespace relational return; // Ignore transient bases. names (c); - inherits (c); + + // If we already found a match in one of the derived classes, + // don't go into bases to get the standard "hiding" behavior. + // + if (!member_.found_) + inherits (c); } public: @@ -1614,37 +1693,27 @@ namespace relational virtual void traverse (type& m) { - // First see if we have the exact match. - // - if (name_ == m.name ()) + if (exact_) { - if (check (m)) + if (name_ == m.name () && check (m)) { assoc_member am; am.m = &m; am.vo = vo_; members_.push_back (am); + found_ = true; } - - return; } - - // Don't bother with public name matching if we already - // have an exact match. - // - if (members_.empty ()) + else { - if (pub_name_ == context::current ().public_name (m)) + if (pub_name_ == context::current ().public_name (m) && + check (m)) { - if (check (m)) - { - assoc_member am; - am.m = &m; - am.vo = vo_; - pub_members_.push_back (am); - } - - return; + assoc_member am; + am.m = &m; + am.vo = vo_; + pub_members_.push_back (am); + found_ = true; } } } @@ -1654,7 +1723,9 @@ namespace relational { // Make sure that the found node can possibly match. // - if (context::transient (m) || context::inverse (m)) + if (context::transient (m) || + context::inverse (m) || + m.count ("polymorphic-ref")) return false; return check_types (utype (m), type_); @@ -1668,6 +1739,8 @@ namespace relational semantics::type& type_; view_object* vo_; + bool exact_; + bool found_; }; traversal::names names_; @@ -1686,7 +1759,31 @@ namespace relational struct class_: traversal::class_, context { class_ () + : std_string_ (0), std_string_hint_ (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? + *this >> member_names_ >> member_; } @@ -1698,17 +1795,115 @@ namespace relational if (k == class_other) return; - names (c); - // Assign pointer. // if (k == class_object || k == class_view) assign_pointer (c); - // Do some additional processing for views. + // Do some additional pre-processing for objects. + // + if (k == class_object) + traverse_object_pre (c); + + names (c); + + // Do some additional post-processing for views. // if (k == class_view) - traverse_view (c); + traverse_view_post (c); + } + + // + // Object. + // + + virtual void + traverse_object_pre (type& c) + { + if (semantics::class_* root = polymorphic (c)) + { + using namespace semantics; + + semantics::data_member& idm (*id_member (*root)); + + if (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)); + + // 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 (!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 (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, *root); + assert (root->pointed_p ()); + } + + unit.new_edge (m, 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))); + + // 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); + } + } } // @@ -1726,7 +1921,7 @@ namespace relational typedef vector relationships; virtual void - traverse_view (type& c) + traverse_view_post (type& c) { bool has_q (c.count ("query")); bool has_o (c.count ("objects")); @@ -1859,12 +2054,46 @@ namespace relational << "persistent class '" << i->obj_name << "' is used in " << "the view more than once" << endl; + error (omap[n]->loc) + << "previously used here" << endl; + info (i->loc) << "use the alias clause to assign it a different name" << endl; throw operation_failed (); } + + // Also add the bases of a polymorphic object. + // + class_* poly_root (polymorphic (o)); + + if (poly_root != 0 && poly_root != &o) + { + for (class_* b (&polymorphic_base (o));; + b = &polymorphic_base (*b)) + { + view_object_map::value_type v (b->tree_node (), &*i); + if (!omap.insert (v).second) + { + error (i->loc) + << "base class '" << class_name (*b) << "' is " + << "used in the view more than once" << endl; + + error (omap[v.first]->loc) + << "previously used here" << endl; + + info (i->loc) + << "use the alias clause to assign it a different name" + << endl; + + throw operation_failed (); + } + + if (b == poly_root) + break; + } + } } else { @@ -1908,9 +2137,7 @@ namespace relational } // Now see if this object points to any of the objects - // specified prior to it. Ignore self-references if any, - // since they were already added to the list in the - // previous pass. + // specified prior to it. // { relationship_resolver r (rs, *j, false); @@ -2002,10 +2229,20 @@ namespace relational { relationship_resolver (relationships& rs, view_object& pointee, - bool self_pointer) - : object_members_base (false, false, true), + bool forward) + // Look in polymorphic bases only for previously-associated + // objects since backward pointers from bases will result in + // the pathological case (we will have to join the base table + // first, which means we will get both bases and derived objects + // instead of just derived). + // + : object_members_base (false, false, true, forward), relationships_ (rs), - self_pointer_ (self_pointer), + // Ignore self-references if we are looking for backward + // pointers since they were already added to the list in + // the previous pass. + // + self_pointer_ (forward), pointer_ (0), pointee_ (pointee) { @@ -2021,6 +2258,11 @@ namespace relational virtual void traverse_pointer (semantics::data_member& m, semantics::class_& c) { + // Ignore polymorphic id references. + // + if (m.count ("polymorphic-ref")) + return; + // Ignore inverse sides of the same relationship to avoid // phony conflicts caused by the direct side that will end // up in the relationship list as well. @@ -2089,13 +2331,31 @@ namespace relational 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")) { - class_pointer const& cp (c.get ("pointer")); - string const& p (cp.name); + 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) @@ -2110,7 +2370,7 @@ namespace relational // 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); + decl = resolve_name (p, cp->scope, true); int tc (TREE_CODE (decl)); if (tc == TYPE_DECL) @@ -2135,10 +2395,11 @@ namespace relational { ptr = p + "< " + type + " >"; decl_name = p; + cp_template = true; } else { - error (cp.loc) + error (cp->loc) << "name '" << p << "' specified with db pragma pointer " << "does not name a type or a template" << endl; @@ -2148,8 +2409,8 @@ namespace relational // Resolve scope is the scope of the pragma. // - resolve_scope = cp.scope; - loc = cp.loc; + resolve_scope = cp->scope; + loc = cp->loc; } else { @@ -2176,8 +2437,8 @@ namespace relational continue; } - class_pointer const& cp (ns->get ("pointer")); - string const& p (cp.name); + cp = &ns->get ("pointer"); + string const& p (cp->name); // Namespace-specified pointer can only be '*' or are template. // @@ -2185,13 +2446,13 @@ namespace relational ptr = type + "*"; else if (p[p.size () - 1] == '*') { - error (cp.loc) + 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) + error (cp->loc) << "name '" << p << "' specified with db pragma pointer " << "at namespace level cannot be a template-id" << endl; } @@ -2199,7 +2460,7 @@ namespace relational { // Resolve this name and make sure it is a template. // - decl = resolve_name (p, cp.scope, true); + decl = resolve_name (p, cp->scope, true); int tc (TREE_CODE (decl)); if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl)) @@ -2209,7 +2470,7 @@ namespace relational } else { - error (cp.loc) + error (cp->loc) << "name '" << p << "' specified with db pragma pointer " << "does not name a template" << endl; } @@ -2218,10 +2479,12 @@ namespace relational if (ptr.empty ()) throw operation_failed (); + cp_template = true; + // Resolve scope is the scope of the pragma. // - resolve_scope = cp.scope; - loc = cp.loc; + resolve_scope = cp->scope; + loc = cp->loc; break; } @@ -2245,18 +2508,18 @@ namespace relational } } + // 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 ()) { - if (!unit.count ("tr1-pointer-used")) - { - unit.set ("tr1-pointer-used", false); - unit.set ("boost-pointer-used", false); - } - - bool& tr1 (unit.get ("tr1-pointer-used")); - bool& boost (unit.get ("boost-pointer-used")); + bool& tr1 (features.tr1_pointer); + bool& boost (features.boost_pointer); // First check the user-supplied name. // @@ -2459,6 +2722,9 @@ namespace relational data_member member_; traversal::names member_names_; + + semantics::type* std_string_; + semantics::names* std_string_hint_; }; } diff --git a/odb/relational/schema-source.hxx b/odb/relational/schema-source.hxx index a72b55b..82b7e71 100644 --- a/odb/relational/schema-source.hxx +++ b/odb/relational/schema-source.hxx @@ -21,18 +21,20 @@ namespace relational virtual void traverse (type& c) { - if (class_file (c) != unit.file () || !object (c) || abstract (c)) + if (class_file (c) != unit.file ()) return; - context::top_object = context::cur_object = &c; + if (!object (c)) + return; + + if (abstract (c) && !polymorphic (c)) + return; os << "// " << class_name (c) << endl << "//" << endl << endl; schema_->traverse (c); - - context::top_object = context::cur_object = 0; } private: diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx index 6067e63..5bf14f1 100644 --- a/odb/relational/source.cxx +++ b/odb/relational/source.cxx @@ -12,6 +12,3251 @@ using namespace std; +void relational::source::class_:: +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 has_ptr (has_a (c, test_pointer)); + + 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); + size_t poly_depth (poly_derived ? polymorphic_depth (c) : 1); + data_member* discriminator (poly ? context::discriminator (*poly_root) : 0); + + bool abst (abstract (c)); + bool reuse_abst (abst && !poly); + + bool grow (false); + bool grow_id (false); + + if (generate_grow) + { + grow = context::grow (c); + grow_id = (id ? context::grow (*id) : false) || + (optimistic ? context::grow (*optimistic) : false); + } + + string const& type (class_fq_name (c)); + string traits ("access::object_traits< " + type + " >"); + column_count_type const& cc (column_count (c)); + + os << "// " << class_name (c) << endl + << "//" << endl + << endl; + + object_extra (c); + + // + // Query. + // + + if (options.generate_query ()) + { + // query_columns_base + // + if (has_ptr) + { + instance t (c); + t->traverse (c); + } + } + + // + // Containers (abstract and concrete). + // + bool containers (has_a (c, test_container)); + bool straight_containers (false); + bool straight_readwrite_containers (false); + + if (containers) + { + containers = true; + size_t scn (has_a (c, test_straight_container)); + + if (scn != 0) + { + straight_containers = true; + + // Inverse containers cannot be marked readonly. + // + straight_readwrite_containers = scn > has_a (c, test_readonly_container); + } + } + + if (containers) + { + instance t (c); + t->traverse (c); + } + + // + // Functions (abstract and concrete). + // + + // id (image_type) + // + if (!poly_derived && id != 0 && !base_id) + { + if (options.generate_query ()) + { + os << traits << "::id_type" << endl + << traits << "::" << endl + << "id (const image_type& i)" + << "{" + << db << "::database* db (0);" + << "ODB_POTENTIALLY_UNUSED (db);" + << endl + << "id_type id;"; + init_id_value_member_->traverse (*id); + os << "return id;" + << "}"; + } + + if (optimistic != 0) + { + os << traits << "::version_type" << endl + << traits << "::" << endl + << "version (const image_type& i)" + << "{" + << "version_type v;"; + init_version_value_member_->traverse (*optimistic); + os << "return v;" + << "}"; + } + } + + // discriminator() + // + if (poly && !poly_derived) + { + os << traits << "::discriminator_type" << endl + << traits << "::" << endl + << "discriminator (const image_type& i)" + << "{" + << db << "::database* db (0);" + << "ODB_POTENTIALLY_UNUSED (db);" + << endl + << "discriminator_type d;"; + init_discriminator_value_member_->traverse (*discriminator); + os << "return d;" + << "}"; + } + + // grow () + // + if (generate_grow) + { + os << "bool " << traits << "::" << endl + << "grow (image_type& i, " << truncated_vector << " t"; + + if (poly_derived) + os << ", std::size_t d"; + + os << ")" + << "{" + << "ODB_POTENTIALLY_UNUSED (i);" + << "ODB_POTENTIALLY_UNUSED (t);" + << endl + << "bool grew (false);" + << endl; + + index_ = 0; + + if (poly_derived) + { + // Select column count for this class. + // + size_t cols (cc.total - cc.id); + + os << "// " << class_name (*poly_base) << " base" << endl + << "//" << endl + << "if (--d != 0)" + << "{" + << "if (object_traits::grow (*i.base, " << + "t + " << cols << "UL" << + (poly_base != poly_root ? ", d" : "") << "))" << endl + << "i.base->version++;" + << "}"; + } + else + inherits (c, grow_base_inherits_); + + names (c, grow_member_names_); + + os << "return grew;" + << "}"; + } + + // bind (image_type) + // + os << "void " << traits << "::" << endl + << "bind (" << bind_vector << " b," << endl; + + // If we are a derived type in a polymorphic hierarchy, then + // we get the the external id binding. + // + if (poly_derived) + os << "const " << bind_vector << " id," << endl + << "std::size_t id_size," << endl; + + os << "image_type& i," << endl + << db << "::statement_kind sk)" + << "{" + << "ODB_POTENTIALLY_UNUSED (sk);" + << endl + << "using namespace " << db << ";" + << endl; + + if (readonly (c)) + os << "assert (sk != statement_update);" + << endl; + + os << "std::size_t n (0);" + << endl; + + if (poly_derived) + { + // The id reference comes first in the insert statement. + // + os << "// " << id->name () << endl + << "//" << endl + << "if (sk == statement_insert)" + << "{" + << "if (id != 0)" << endl + << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));" + << "n += id_size;" + << "}"; + } + else + inherits (c, bind_base_inherits_); + + names (c, bind_member_names_); + + if (poly_derived) + { + // The id reference comes last in the update statement. + // + if (!readonly (c)) + os << "// " << id->name () << endl + << "//" << endl + << "if (sk == statement_update)" + << "{" + << "if (id != 0)" << endl + << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));" + << "n += id_size;" + << "}"; + + // Bind the image chain for the select statement. Seeing that + // this is the last statement in the function, we don't care + // about updating n. + // + os << "// " << class_name (*poly_base) << " base" << endl + << "//" << endl + << "if (sk == statement_select)" << endl + << "object_traits::"; + + if (poly_base == poly_root) + os << "bind (b + n, *i.base, sk);"; + else + os << "bind (b + n, id, id_size, *i.base, sk);"; + } + + os << "}"; + + // bind (id_image_type) + // + if (!poly_derived && id != 0 && !base_id) + { + os << "void " << traits << "::" << endl + << "bind (" << bind_vector << " b, id_image_type& i" << + (optimistic != 0 ? ", bool bv" : "") << ")" + << "{" + << "std::size_t n (0);"; + + if (composite_wrapper (utype (*id))) + os << db << "::statement_kind sk (" << db << "::statement_select);"; + + bind_id_member_->traverse (*id); + + if (optimistic != 0) + { + os << "if (bv)" + << "{" + << "n += " << column_count (c).id << ";" + << endl; + + bind_version_member_->traverse (*optimistic); + os << "}"; + } + + os << "}"; + } + + // init (image, object) + // + os << "bool " << traits << "::" << endl + << "init (image_type& i, const object_type& o, " << + db << "::statement_kind sk)" + << "{" + << "ODB_POTENTIALLY_UNUSED (i);" + << "ODB_POTENTIALLY_UNUSED (o);" + << "ODB_POTENTIALLY_UNUSED (sk);" + << endl + << "using namespace " << db << ";" + << endl; + + if (readonly (c)) + os << "assert (sk != statement_update);" + << endl; + + init_image_pre (c); + + os << "bool grew (false);" + << endl; + + if (!poly_derived) + inherits (c, init_image_base_inherits_); + + names (c, init_image_member_names_); + + os << "return grew;" + << "}"; + + // init (object, image) + // + os << "void " << traits << "::" << endl + << "init (object_type& o, const image_type& i, database* db"; + + if (poly_derived) + os << ", std::size_t d"; + + os << ")" + << "{" + << "ODB_POTENTIALLY_UNUSED (o);" + << "ODB_POTENTIALLY_UNUSED (i);" + << "ODB_POTENTIALLY_UNUSED (db);" + << endl; + + if (poly_derived) + { + os << "// " << class_name (*poly_base) << " base" << endl + << "//" << endl + << "if (--d != 0)" << endl + << "object_traits::init (o, *i.base, db" << + (poly_base != poly_root ? ", d" : "") << ");" + << endl; + } + else + inherits (c, init_value_base_inherits_); + + names (c, init_value_member_names_); + + os << "}"; + + // init (id_image, id) + // + if (id != 0 && !base_id) + { + os << "void " << traits << "::" << endl + << "init (id_image_type& i, const id_type& id" << + (optimistic != 0 ? ", const version_type* v" : "") << ")" + << "{"; + + if (grow_id) + os << "bool grew (false);"; + + if (composite_wrapper (utype (*id))) + os << db << "::statement_kind sk (" << db << "::statement_select);"; + + init_id_image_member_->traverse (*id); + + if (optimistic != 0) + { + // Here we rely on the fact that init_image_member + // always wraps the statements in a block. + // + os << "if (v != 0)"; + init_version_image_member_->traverse (*optimistic); + } + + if (grow_id) + os << "if (grew)" << endl + << "i.version++;"; + + os << "}"; + } + + // The rest does not apply to reuse-abstract objects. + // + if (reuse_abst) + return; + + // + // Containers (concrete). + // + + // Statement cache (definition). + // + if (id != 0) + { + os << "struct " << traits << "::container_statement_cache_type" + << "{"; + + instance cm; + cm->traverse (c); + + os << (containers ? "\n" : "") + << "container_statement_cache_type (" << db << "::connection&" << + (containers ? " c" : "") << ")"; + + instance im; + im->traverse (c); + + os << "{" + << "}" + << "};"; + } + + // Polymorphic map. + // + if (poly) + { + if (!poly_derived) + os << traits << "::map_type*" << endl + << traits << "::map;" + << endl; + + os << traits << "::" << (abst ? "abstract_" : "") << "info_type" << endl + << traits << "::info (" << endl + << "typeid (" << type << ")," << endl; + + if (poly_derived) + os << "&object_traits< " << class_fq_name (*poly_base) << " >::info"; + else + os << "0"; + + if (!abst) + { + string n (class_fq_name (c)); + + os << "," << endl + << strlit (string (n, 2, string::npos)) << "," << endl + << "&odb::create_impl< " << type << " >," << endl + << "&odb::dispatch_impl< " << type << " >," << endl; + + if (poly_derived) + os << "&statements_type::delayed_loader"; + else + os << "0"; + } + + os << ");" + << endl; + + if (!abst) + os << traits << "::entry_type" << endl + << traits << "::entry;" + << endl; + } + + // + // Statements. + // + + string const& table (table_qname (c)); + + // persist_statement + // + { + statement_columns sc; + { + statement_kind sk (statement_insert); // Imperfect forwarding. + instance ct (sk, sc); + ct->traverse (c); + process_statement_columns (sc, statement_insert); + } + + bool dv (sc.empty ()); // The DEFAULT VALUES syntax. + + os << "const char " << traits << "::persist_statement[] =" << endl + << strlit ("INSERT INTO " + table_qname(c) + (dv ? "" : " (")) << endl; + + for (statement_columns::const_iterator i (sc.begin ()), + e (sc.end ()); i != e;) + { + string const& c (i->column); + os << strlit (c + (++i != e ? "," : ")")) << endl; + } + + instance qp; + + persist_statement_extra (c, *qp, persist_after_columns); + + if (!dv) + { + string values; + instance pt (values, *qp); + pt->traverse (c); + os << strlit (" VALUES (" + values + ")"); + } + else + os << strlit (" DEFAULT VALUES"); + + persist_statement_extra (c, *qp, persist_after_values); + + os << ";" + << endl; + } + + if (id != 0) + { + instance id_cols; + id_cols->traverse (*id); + + std::vector find_column_counts (abst ? 1 : poly_depth); + + // find_statement + // + if (poly_derived) + os << "const char* const " << traits << "::find_statements[] =" + << "{"; + else + os << "const char " << traits << "::find_statement[] =" << endl; + + for (size_t d (poly_depth); d != 0;) + { + statement_columns sc; + { + statement_kind sk (statement_select); // Imperfect forwarding. + instance t (table, sk, sc, d); + t->traverse (c); + process_statement_columns (sc, statement_select); + find_column_counts[poly_depth - d] = sc.size (); + } + + os << strlit ("SELECT ") << endl; + + for (statement_columns::const_iterator i (sc.begin ()), + e (sc.end ()); i != e;) + { + string const& c (i->column); + os << strlit (c + (++i != e ? "," : "")) << endl; + } + + os << strlit (" FROM " + table) << endl; + + if (poly_derived) + { + instance j (c, d); + j->traverse (c); + } + + bool f (false); // @@ (im)perfect forwarding + instance j (c, f, d); // @@ (im)perfect forwarding + j->traverse (c); + + instance qp; + for (object_columns_list::iterator b (id_cols->begin ()), i (b); + i != id_cols->end (); ++i) + { + if (i != b) + os << endl; + + os << strlit ((i == b ? " WHERE " : " AND ") + table + "." + + quote_id (i->name) + "=" + qp->next ()); + } + + if (abst) + break; + + if (--d != 0) + os << "," << endl + << endl; + } + + if (poly_derived) + os << "};"; + else + os << ";" + << endl; + + // find_column_counts + // + if (poly_derived) + { + os << "const std::size_t " << traits << "::find_column_counts[] =" + << "{"; + + for (std::vector::iterator i (find_column_counts.begin ()), + e (find_column_counts.end ()); i != e;) + { + os << *i << "UL"; + + if (++i != e) + os << ',' << endl; + } + + os << "};"; + } + + // find_discriminator_statement + // + if (poly && !poly_derived) + { + statement_columns sc; + { + statement_kind sk (statement_select); // Imperfect forwarding. + instance t (table, sk, sc); + t->traverse (*discriminator); + + if (optimistic != 0) + t->traverse (*optimistic); + + process_statement_columns (sc, statement_select); + } + + os << "const char " << traits << "::" << endl + << "find_discriminator_statement[] =" << endl + << strlit ("SELECT ") << endl; + + for (statement_columns::const_iterator i (sc.begin ()), + e (sc.end ()); i != e;) + { + string const& c (i->column); + os << strlit (c + (++i != e ? "," : "")) << endl; + } + + os << strlit (" FROM " + table) << endl; + + instance qp; + for (object_columns_list::iterator b (id_cols->begin ()), i (b); + i != id_cols->end (); ++i) + { + if (i != b) + os << endl; + + os << strlit ((i == b ? " WHERE " : " AND ") + table + "." + + quote_id (i->name) + "=" + qp->next ()); + } + + os << ";" + << endl; + } + + // update_statement + // + if (cc.total != cc.id + cc.inverse + cc.readonly) + { + instance qp; + + statement_columns sc; + { + query_parameters* p (qp.get ()); // Imperfect forwarding. + statement_kind sk (statement_update); // Imperfect forwarding. + instance t (sk, sc, p); + t->traverse (c); + process_statement_columns (sc, statement_update); + } + + os << "const char " << traits << "::update_statement[] =" << endl + << strlit ("UPDATE " + table + " SET ") << endl; + + for (statement_columns::const_iterator i (sc.begin ()), + e (sc.end ()); i != e;) + { + string const& c (i->column); + os << strlit (c + (++i != e ? "," : "")) << endl; + } + + for (object_columns_list::iterator b (id_cols->begin ()), i (b); + i != id_cols->end (); ++i) + { + if (i != b) + os << endl; + + os << strlit ((i == b ? " WHERE " : " AND ") + + quote_id (i->name) + "=" + qp->next ()); + } + + if (optimistic != 0 && !poly_derived) + os << endl + << strlit (" AND " + column_qname (*optimistic) + + "=" + qp->next ()); + os << ";" + << endl; + } + + // erase_statement + // + { + instance qp; + os << "const char " << traits << "::erase_statement[] =" << endl + << strlit ("DELETE FROM " + table); + + for (object_columns_list::iterator b (id_cols->begin ()), i (b); + i != id_cols->end (); ++i) + { + + os << endl + << strlit ((i == b ? " WHERE " : " AND ") + + quote_id (i->name) + "=" + qp->next ()); + } + + os << ";" + << endl; + } + + if (optimistic != 0 && !poly_derived) + { + instance qp; + + os << "const char " << traits << "::optimistic_erase_statement[] " << + "=" << endl + << strlit ("DELETE FROM " + table); + + for (object_columns_list::iterator b (id_cols->begin ()), i (b); + i != id_cols->end (); ++i) + { + os << endl + << strlit ((i == b ? " WHERE " : " AND ") + + quote_id (i->name) + "=" + qp->next ()); + } + + os << endl + << strlit (" AND " + column_qname (*optimistic) + + "=" + qp->next ()) << ";" + << endl; + } + } + + if (options.generate_query ()) + { + // query_statement + // + statement_columns sc; + { + statement_kind sk (statement_select); // Imperfect forwarding. + instance oc (table, sk, sc, poly_depth); + oc->traverse (c); + process_statement_columns (sc, statement_select); + } + + os << "const char " << traits << "::query_statement[] =" << endl + << strlit ("SELECT ") << endl; + + for (statement_columns::const_iterator i (sc.begin ()), + e (sc.end ()); i != e;) + { + string const& c (i->column); + os << strlit (c + (++i != e ? "," : "")) << endl; + } + + os << strlit (" FROM " + table) << endl; + + if (poly_derived) + { + instance j (c, poly_depth); + j->traverse (c); + } + + if (id != 0) + { + bool t (true); //@@ (im)perfect forwarding + instance oj (c, t, poly_depth); //@@ (im)perfect forwarding + oj->traverse (c); + } + + os << strlit (" ") << ";" + << endl; + + // erase_query_statement + // + os << "const char " << traits << "::erase_query_statement[] =" << endl + << strlit ("DELETE FROM " + table) << endl; + + // DELETE JOIN: + // + // MySQL: + // << strlit ("DELETE FROM " + table + " USING " + table) << endl; + // << strlit ("DELETE " + table + " FROM " + table) << endl; + // oj->write (); + // + + os << strlit (" ") << ";" + << endl; + + // table_name + // + os << "const char " << traits << "::table_name[] =" << endl + << strlit (table_qname (c)) << ";" // Use quoted name. + << endl; + } + + // persist () + // + os << "void " << traits << "::" << endl + << "persist (database& db, " << (auto_id ? "" : "const ") << + "object_type& obj"; + + if (poly) + os << ", bool top, bool dyn"; + + os << ")" + << "{" + << "ODB_POTENTIALLY_UNUSED (db);"; + + if (poly) + os << "ODB_POTENTIALLY_UNUSED (top);"; + + os << endl + << "using namespace " << db << ";" + << endl; + + if (poly) + os << "if (dyn)" << endl + << "{" + << "const std::type_info& t (typeid (obj));" + << endl + << "if (t != info.type)" + << "{" + << "const info_type& pi (root_traits::map->find (t));" + << "pi.dispatch (info_type::call_persist, db, &obj, 0);" + << "return;" + << "}" + << "}"; + + // If we are database-poly-abstract but not C++-abstract, then make + // sure we are not trying to persist an instance of an abstract class. + // + if (abst && !c.abstract ()) + os << "if (top)" << endl + << "throw abstract_class ();" + << endl; + + os << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << "statements_type& sts (" << endl + << "conn.statement_cache ().find_object ());" + << endl; + + // Call callback (pre_persist). + // + if (!abst) // If we are poly-abstract, then top will always be false. + { + if (poly) + os << "if (top)" << endl; + + os << "callback (db," << endl + << (auto_id ? "static_cast (obj)," : "obj,") << endl + << "callback_event::pre_persist);" + << endl; + } + + // Call our base if we are a derived type in a polymorphic + // hierarchy. + // + if (poly_derived) + os << "object_traits::persist (db, obj, false, false);" + << endl; + + os << "image_type& im (sts.image ());" + << "binding& imb (sts.insert_image_binding ());"; + + if (poly_derived) + os << "const binding& idb (sts.id_image_binding ());"; + + os << endl + << "if (init (im, obj, statement_insert))" << endl + << "im.version++;" + << endl; + + if (!poly_derived && auto_id && insert_send_auto_id) + { + string const& n (id->name ()); + string var ("im." + n + (n[n.size () - 1] == '_' ? "" : "_")); + init_auto_id (*id, var); + os << endl; + } + + os << "if ("; + + if (poly_derived) + os << "idb.version != sts.insert_id_binding_version () ||" << endl; + + os << "im.version != sts.insert_image_version () ||" << endl + << "imb.version == 0)" + << "{" + << "bind (imb.bind, "; + + if (poly_derived) + os << "idb.bind, idb.count, "; + + os << "im, statement_insert);"; + + if (poly_derived) + os << "sts.insert_id_binding_version (idb.version);"; + + os << "sts.insert_image_version (im.version);" + << "imb.version++;" + << "}"; + + os << "insert_statement& st (sts.persist_statement ());" + << "if (!st.execute ())" << endl + << "throw object_already_persistent ();" + << endl; + + if (!poly_derived && auto_id) + { + if (const_type (id->type ())) + os << "const_cast< id_type& > (obj." << id->name () << ")"; + else + os << "obj." << id->name (); + + os << " = static_cast< id_type > (st.id ());" + << endl; + } + + if (optimistic != 0 && !poly_derived) + { + // Set the version in the object member. + // + if (!auto_id || const_type (optimistic->type ())) + os << "const_cast< version_type& > (obj." << optimistic->name () << + ") = 1;"; + else + os << "obj." << optimistic->name () << " = 1;"; + + os << endl; + } + + // Initialize id_image and binding if we are a root of a polymorphic + // hierarchy or if we have non-inverse containers. + // + if (!poly_derived && (poly || straight_containers)) + { + // If this is a polymorphic root without containers, then we only + // need to do this if we are not a top-level call. If we are poly- + // abstract, then top will always be false. + // + if (poly && !straight_containers && !abst) + os << "if (!top)" + << "{"; + + os << "id_image_type& i (sts.id_image ());" + << "init (i, obj." << id->name () << ");" + << endl + << "binding& idb (sts.id_image_binding ());" + << "if (i.version != sts.id_image_version () || idb.version == 0)" + << "{" + << "bind (idb.bind, i);" + << "sts.id_image_version (i.version);" + << "idb.version++;" + << "}"; + + if (poly && !straight_containers && !abst) + os << "}"; + } + + if (straight_containers) + { + instance t (container_calls::persist_call); + t->traverse (c); + } + + // Call callback (post_persist). + // + if (!abst) // If we are poly-abstract, then top will always be false. + { + if (poly) + os << "if (top)" << endl; + + os << "callback (db," << endl + << (auto_id ? "static_cast (obj)," : "obj,") << endl + << "callback_event::post_persist);"; + } + + os << "}"; + + // update () + // + bool readonly (context::readonly (c)); + + if (id != 0 && (!readonly || poly)) + { + os << "void " << traits << "::" << endl + << "update (database& db, const object_type& obj"; + + if (poly) + os << ", bool top, bool dyn"; + + os << ")" + << "{" + << "ODB_POTENTIALLY_UNUSED (db);"; + + if (poly) + os << "ODB_POTENTIALLY_UNUSED (top);"; + + os << endl + << "using namespace " << db << ";" + << endl; + + if (poly) + os << "if (dyn)" << endl + << "{" + << "const std::type_info& t (typeid (obj));" + << endl + << "if (t != info.type)" + << "{" + << "const info_type& pi (root_traits::map->find (t));" + << "pi.dispatch (info_type::call_update, db, &obj, 0);" + << "return;" + << "}" + << "}"; + + // If we are database-poly-abstract but not C++-abstract, then make + // sure we are not trying to update an instance of an abstract class. + // + if (abst && !c.abstract ()) + os << "if (top)" << endl + << "throw abstract_class ();" + << endl; + + // If we are readonly, then there is nothing else to do. + // + if (!readonly) + { + // Call callback (pre_update). + // + if (!abst) // If we are poly-abstract then top will always be false. + { + if (poly) + os << "if (top)" << endl; + + os << "callback (db, obj, callback_event::pre_update);" + << endl; + } + + if (poly_derived) + { + bool readonly_base (context::readonly (*poly_base)); + + if (readonly_base || + cc.total != cc.id + cc.inverse + cc.readonly || + straight_readwrite_containers) + { + os << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << "statements_type& sts (" << endl + << "conn.statement_cache ().find_object ());" + << endl; + } + + // Unless our base is readonly, call it first. + // + if (!readonly_base) + { + os << "object_traits::update (db, obj, false, false);" + << endl; + } + else + { + // Otherwise, we have to initialize the id image ourselves. If + // we don't have any columns or containers to update, then we + // only have to do it if this is not a top-level call. If we + // are abstract, then top is always false. + // + if (cc.total == cc.id + cc.inverse + cc.readonly && + !straight_readwrite_containers && + !abst) + os << "if (!top)"; + + os << "{" + << "id_image_type& i (sts.id_image ());" + << "init (i, obj." << id->name () << ");" + << endl; + + os << "binding& idb (sts.id_image_binding ());" + << "if (i.version != sts.id_image_version () || idb.version == 0)" + << "{" + << "bind (idb.bind, i);" + << "sts.id_image_version (i.version);" + << "idb.version++;" + << "}" + << "}"; + } + + if (cc.total != cc.id + cc.inverse + cc.readonly || + straight_readwrite_containers) + { + os << "const binding& idb (sts.id_image_binding ());" + << endl; + } + + if (cc.total != cc.id + cc.inverse + cc.readonly) + { + // Initialize the object image. + // + os << "image_type& im (sts.image ());" + << "if (init (im, obj, statement_update))" << endl + << "im.version++;" + << endl; + + os << "binding& imb (sts.update_image_binding ());" + << "if (idb.version != sts.update_id_binding_version () ||" << endl + << "im.version != sts.update_image_version () ||" << endl + << "imb.version == 0)" + << "{" + << "bind (imb.bind, idb.bind, idb.count, im, statement_update);" + << "sts.update_id_binding_version (idb.version);" + << "sts.update_image_version (im.version);" + << "imb.version++;" + << "}"; + + os << "if (sts.update_statement ().execute () == 0)" << endl + << "throw object_not_persistent ();" + << endl; + } + else if (readonly_base) + { + // If our base is readonly and we don't have any columns to + // update, then we have to make sure this object actually + // exists in the database. Use the discriminator_() call for + // that. + // + os << "root_traits::discriminator_ (sts.root_statements (), obj." << + id->name () << ", 0);" + << endl; + } + // Otherwise, nothing else to do here if we don't have any columns + // to update. + // + } + else if (cc.total != cc.id + cc.inverse + cc.readonly) + { + os << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << "statements_type& sts (" << endl + << "conn.statement_cache ().find_object ());" + << endl; + + // Initialize object and id images. + // + os << "id_image_type& i (sts.id_image ());"; + + if (optimistic == 0) + os << "init (i, obj." << id->name () << ");"; + else + os << "init (i, obj." << id->name () << ", &obj." << + optimistic->name () << ");"; + + os << endl + << "image_type& im (sts.image ());" + << "if (init (im, obj, statement_update))" << endl + << "im.version++;" + << endl; + + // Update binding is bound to two images (object and id) + // so we have to track both versions. + // + os << "bool u (false);" // Avoid incrementing version twice. + << "binding& imb (sts.update_image_binding ());" + << "if (im.version != sts.update_image_version () ||" << endl + << "imb.version == 0)" + << "{" + << "bind (imb.bind, im, statement_update);" + << "sts.update_image_version (im.version);" + << "imb.version++;" + << "u = true;" + << "}"; + + // To update the id part of the update binding we have to do + // it indirectly via the id binding, which just points to the + // suffix of the update bind array (see object_statements). + // + os << "binding& idb (sts.id_image_binding ());" + << "if (i.version != sts.update_id_image_version () ||" << endl + << "idb.version == 0)" + << "{" + // If the id binding is up-to-date, then that means update + // binding is too and we just need to update the versions. + // + << "if (i.version != sts.id_image_version () ||" << endl + << "idb.version == 0)" + << "{" + << "bind (idb.bind, i);" + // Update the id binding versions since we may use them later + // to update containers. + // + << "sts.id_image_version (i.version);" + << "idb.version++;" + << "}" + << "sts.update_id_image_version (i.version);" + << endl + << "if (!u)" << endl + << "imb.version++;" + << "}"; + + os << "if (sts.update_statement ().execute () == 0)" << endl; + + if (optimistic == 0) + os << "throw object_not_persistent ();"; + else + os << "throw object_changed ();"; + + os << endl; + } + else + { + // We don't have any columns to update. Note that we still have + // to make sure this object exists in the database. For that we + // will run the SELECT query using the find_() function. + // + // + os << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << "statements_type& sts (" << endl + << "conn.statement_cache ().find_object ());" + << endl; + + if (poly) + { + // In case of a polymorphic root, use discriminator_(), which + // is faster. And initialize the id image, unless this is a + // top-level call. + // + os << "discriminator_ (sts, obj." << id->name () << ", 0);" + << endl; + + if (!abst) + os << "if (!top)"; + + os << "{" + << "id_image_type& i (sts.id_image ());" + << "init (i, obj." << id->name () << ");" + << endl; + + os << "binding& idb (sts.id_image_binding ());" + << "if (i.version != sts.id_image_version () || idb.version == 0)" + << "{" + << "bind (idb.bind, i);" + << "sts.id_image_version (i.version);" + << "idb.version++;" + << "}" + << "}"; + } + else + { + os << "if (!find_ (sts, &obj." << id->name () << "))" << endl + << "throw object_not_persistent ();" + << endl; + + if (delay_freeing_statement_result) + os << "sts.find_statement ().free_result ();"; + } + + if (straight_readwrite_containers) + os << "binding& idb (sts.id_image_binding ());" + << endl; + } + + if (straight_readwrite_containers) + { + instance t (container_calls::update_call); + t->traverse (c); + } + + if (optimistic != 0 && !poly_derived) + { + // Update version in the object member. + // + os << "const_cast (obj." << optimistic->name () << ")++;"; + } + + // Call callback (post_update). + // + if (!abst) // If we are poly-abstract, then top will always be false. + { + if (poly) + os << "if (top)" << endl; + + os << "callback (db, obj, callback_event::post_update);"; + } + } // readonly + + os << "}"; + } + + // erase (id) + // + if (id != 0) + { + os << "void " << traits << "::" << endl + << "erase (database& db, const id_type& id"; + + if (poly) + os << ", bool top, bool dyn"; + + os << ")" + << "{" + << "using namespace " << db << ";" + << endl + << "ODB_POTENTIALLY_UNUSED (db);"; + + if (poly) + os << "ODB_POTENTIALLY_UNUSED (top);"; + + os << endl + << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << "statements_type& sts (" << endl + << "conn.statement_cache ().find_object ());" + << endl; + + // Get the discriminator and determine the dynamic type of + // this object. + // + if (poly) + { + os << "if (dyn)" << endl + << "{" + << "discriminator_type d;" + << "root_traits::discriminator_ (sts.root_statements (), id, &d);"; + + if (!abst) + os << endl + << "if (d != info.discriminator)" + << "{"; + + os << "const info_type& pi (root_traits::map->find (d));" + << endl + // Check that the dynamic type is derived from the static type. + // + << "if (!pi.derived (info))" << endl + << "throw object_not_persistent ();" + << endl + << "pi.dispatch (info_type::call_erase, db, 0, &id);" + << "return;"; + + if (!abst) + os << "}"; + + os << "}"; + } + + // Initialize id image. + // + if (!abst) // If we are poly-abstract, then top will always be false. + { + if (poly) + os << "if (top)" + << "{"; + + os << "id_image_type& i (sts.id_image ());" + << "init (i, id);" + << endl; + + os << "binding& idb (sts.id_image_binding ());" + << "if (i.version != sts.id_image_version () || idb.version == 0)" + << "{" + << "bind (idb.bind, i);" + << "sts.id_image_version (i.version);" + << "idb.version++;" + << "}"; + + if (poly) + os << "}"; + } + + // Erase containers first so that there are no reference + // violations (we don't want to rely on ON DELETE CASCADE + // here since in case of a custom schema, it might not be + // there). + // + if (straight_containers) + { + if (poly) + os << "binding& idb (sts.id_image_binding ());" + << endl; + + instance t (container_calls::erase_call); + t->traverse (c); + } + + os << "if (sts.erase_statement ().execute () != 1)" << endl + << "throw object_not_persistent ();" + << endl; + + if (poly_derived) + { + // Call our base last (we erase polymorphic objects from base + // to derived in order not to trigger cascading deletes). + // + os << "object_traits::erase (db, id, false, false);" + << endl; + } + + // Remove from the object cache. + // + if (!abst) // If we are poly-abstract, then top will always be false. + { + if (poly) + os << "if (top)" << endl; + + os << "pointer_cache_traits::erase (db, id);"; + } + + os << "}"; + } + + // erase (object) + // + if (id != 0 && (poly || optimistic != 0)) + { + os << "void " << traits << "::" << endl + << "erase (database& db, const object_type& obj"; + + if (poly) + os << ", bool top, bool dyn"; + + os << ")" + << "{" + << "ODB_POTENTIALLY_UNUSED (db);"; + + if (poly) + os << "ODB_POTENTIALLY_UNUSED (top);"; + + os << endl; + + if (poly) + os << "if (dyn)" << endl + << "{" + << "const std::type_info& t (typeid (obj));" + << endl + << "if (t != info.type)" + << "{" + << "const info_type& pi (root_traits::map->find (t));" + << "pi.dispatch (info_type::call_erase, db, &obj, 0);" + << "return;" + << "}" + << "}"; + + // If we are database-poly-abstract but not C++-abstract, then make + // sure we are not trying to persist an instance of an abstract class. + // + if (abst && !c.abstract ()) + os << "if (top)" << endl + << "throw abstract_class ();" + << endl; + + // Determine the dynamic type of this object. + // + if (optimistic == 0) + { + os << "callback (db, obj, callback_event::pre_erase);" + << "erase (db, id (obj), true, false);" + << "callback (db, obj, callback_event::post_erase);"; + } + else + { + string rsts (poly_derived ? "rsts" : "sts"); + + os << "using namespace " << db << ";" + << endl + << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << "statements_type& sts (" << endl + << "conn.statement_cache ().find_object ());"; + + if (poly_derived) + os << "root_statements_type& rsts (sts.root_statements ());"; + + os << endl; + + // Call callback (pre_erase). + // + if (!abst) // If we are poly-abstract, then top will always be false. + { + if (poly) + os << "if (top)" << endl; + + os << "callback (db, obj, callback_event::pre_erase);" + << endl; + } + + // Initialize id + managed column image. + // + os << "binding& idb (" << rsts << ".id_image_binding ());" + << endl; + + if (!abst) // If we are poly-abstract, then top will always be false. + { + if (poly) + os << "if (top)" + << "{"; + + os << "id_image_type& i (" << rsts << ".id_image ());" + << "init (i, obj." << id->name () << ", &obj." << + optimistic->name () << ");" + << endl; + + // To update the id part of the optimistic id binding we have + // to do it indirectly via the id binding, since both id and + // optimistic id bindings just point to the suffix of the + // update bind array (see object_statements). + // + os << "binding& oidb (" << rsts << ".optimistic_id_image_binding ());" + << "if (i.version != " << rsts << + ".optimistic_id_image_version () ||" << endl + << "oidb.version == 0)" + << "{" + // If the id binding is up-to-date, then that means optimistic + // id binding is too and we just need to update the versions. + // + << "if (i.version != " << rsts << ".id_image_version () ||" << endl + << "idb.version == 0)" + << "{" + << "bind (idb.bind, i);" + // Update the id binding versions since we may use them later + // to delete containers. + // + << rsts << ".id_image_version (i.version);" + << "idb.version++;" + << "}" + << rsts << ".optimistic_id_image_version (i.version);" + << "oidb.version++;" + << "}"; + + if (poly) + os << "}"; // if (top) + } + + // If this is a derived type in a polymorphic hierarchy, then we + // need to check the version (stored in root) before we go ahead + // and start deleting things. Also use the same code for root with + // containers since it is more efficient than the find_() method + // below. + // + if (poly_derived || (poly && straight_containers)) + { + // Only do the check in the top-level call. + // + if (!abst) // If we are poly-abstract, then top will always be false. + { + os << "if (top)" + << "{" + << "version_type v;" + << "root_traits::discriminator_ (" << rsts << ", obj." << + id->name () << ", 0, &v);" + << endl + << "if (v != obj." << optimistic->name () << ")" << endl + << "throw object_changed ();" + << "}"; + } + } + else if (straight_containers) + { + // Things get complicated here: we don't want to trash the + // containers and then find out that the versions don't match + // and we therefore cannot delete the object. After all, there + // is no guarantee that the user will abort the transaction. + // In fact, a perfectly reasonable scenario is to reload the + // object, re-check the condition, decide not to delete the + // object, and then commit the transaction. + // + // There doesn't seem to be anything better than first making + // sure we can delete the object, then deleting the container + // data, and then deleting the object. To check that we can + // delete the object we are going to use find_() and then + // compare the versions. A special-purpose SELECT query would + // have been more efficient but it would complicated and bloat + // things significantly. + // + os << "if (!find_ (sts, &obj." << id->name () << "))" << endl + << "throw object_changed ();" + << endl; + + if (delay_freeing_statement_result) + os << "sts.find_statement ().free_result ();"; + + os << "if (version (sts.image ()) != obj." << + optimistic->name () << ")" << endl + << "throw object_changed ();" + << endl; + } + + // Erase containers first so that there are no reference + // violations (we don't want to rely on ON DELETE CASCADE + // here since in case of a custom schema, it might not be + // there). + // + if (straight_containers) + { + instance t (container_calls::erase_call); + t->traverse (c); + } + + const char* st ( + poly_derived ? "erase_statement" : "optimistic_erase_statement"); + + os << "if (sts." << st << " ().execute () != 1)" << endl + << "throw object_changed ();" + << endl; + + if (poly_derived) + { + // Call our base last (we erase polymorphic objects from base + // to derived in order not to trigger cascading deletes). + // + os << "object_traits::erase (db, obj, false, false);" + << endl; + } + + if (!abst) // If we are poly-abstract, then top will always be false. + { + if (poly) + os << "if (top)" + << "{"; + + // Remove from the object cache. + // + os << "pointer_cache_traits::erase (db, obj." << id->name () << ");"; + + // Call callback (post_erase). + // + os << "callback (db, obj, callback_event::post_erase);"; + + if (poly) + os << "}"; + } + } + + os << "}"; + } + + // find (id) + // + if (id != 0 && c.default_ctor ()) + { + string rsts (poly_derived ? "rsts" : "sts"); + + os << traits << "::pointer_type" << endl + << traits << "::" << endl + << "find (database& db, const id_type& id)" + << "{" + << "using namespace " << db << ";" + << endl; + + // First check the session. + // + os << "{"; + + if (poly_derived) + os << "root_traits::pointer_type rp (pointer_cache_traits::find (" << + "db, id));" + << endl + << "if (!root_traits::pointer_traits::null_ptr (rp))" << endl + << "return" << endl + << " root_traits::pointer_traits::dynamic_pointer_cast<" << + "object_type> (rp);"; + else + os << "pointer_type p (pointer_cache_traits::find (db, id));" + << endl + << "if (!pointer_traits::null_ptr (p))" << endl + << "return p;"; + + os << "}"; + + // Get the connection. + // + os << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << "statements_type& sts (" << endl + << "conn.statement_cache ().find_object ());"; + + if (poly_derived) + os << "root_statements_type& rsts (sts.root_statements ());"; + + os << endl + << "statements_type::auto_lock l (" << rsts << ");"; + + if (delay_freeing_statement_result) + os << "auto_result ar;"; + + if (poly) + os << "root_traits::discriminator_type d;"; + + os << endl + << "if (l.locked ())" + << "{" + << "if (!find_ (sts, &id))" << endl + << "return pointer_type ();"; + + if (delay_freeing_statement_result) + os << endl + << "ar.set (sts.find_statement (" << (poly_derived ? "depth" : "") << + "));"; + + if (poly) + os << "d = root_traits::discriminator (" << rsts << ".image ());"; + + os << "}"; + + if (poly) + { + // If statements are locked, then get the discriminator by + // executing a special SELECT statement. We need it to be + // able to create an object of the correct dynamic type + // which will be loaded later. + // + os << "else" << endl + << "root_traits::discriminator_ (" << rsts << ", id, &d);" + << endl; + + if (abst) + os << "const info_type& pi (root_traits::map->find (d));" + << endl; + else + os << "const info_type& pi (" << endl + << "d == info.discriminator ? info : root_traits::map->find (d));" + << endl; + } + + // Create the object. + // + if (poly_derived) + { + os << "root_traits::pointer_type rp (pi.create ());" + << "pointer_type p (" << endl + << "root_traits::pointer_traits::static_pointer_cast " << + "(rp));" + << "pointer_traits::guard pg (p);" + << endl; + + // Insert it as a root pointer (for non-unique pointers, rp should + // still be valid and for unique pointers this is a no-op). + // + os << "pointer_cache_traits::insert_guard ig (" << endl + << "pointer_cache_traits::insert (db, id, rp));" + << endl; + } + else + { + if (poly) + os << "pointer_type p (pi.create ());"; + else + os << "pointer_type p (" << endl + << "access::object_factory::create ());"; + + os << "pointer_traits::guard pg (p);" + << endl; + + os << "pointer_cache_traits::insert_guard ig (" << endl + << "pointer_cache_traits::insert (db, id, p));" + << endl; + } + + os << "object_type& obj (pointer_traits::get_ref (p));" + << endl + << "if (l.locked ())" + << "{" + << "select_statement& st (sts.find_statement (" << + (poly_derived ? "depth" : "") << "));" + << "ODB_POTENTIALLY_UNUSED (st);" + << endl; + + if (poly) + os << "callback_event ce (callback_event::pre_load);" + << "pi.dispatch (info_type::call_callback, db, &obj, &ce);"; + else + os << "callback (db, obj, callback_event::pre_load);"; + + os << "init (obj, sts.image (), &db);"; + + init_value_extra (); + + if (delay_freeing_statement_result) + os << "ar.free ();"; + + os << "load_ (sts, obj);"; + + if (poly) + // Load the dynamic part of the object unless static and dynamic + // types are the same. + // + os << endl + << "if (&pi != &info)" + << "{" + << "std::size_t d (depth);" + << "pi.dispatch (info_type::call_load, db, &obj, &d);" + << "}"; + + os << rsts << ".load_delayed ();" + << "l.unlock ();"; + + if (poly) + os << "ce = callback_event::post_load;" + << "pi.dispatch (info_type::call_callback, db, &obj, &ce);"; + else + os << "callback (db, obj, callback_event::post_load);"; + + os << "}" + << "else" << endl + << rsts << ".delay_load (id, obj, ig.position ()" << + (poly ? ", pi.delayed_loader" : "") << ");" + << endl; + + os << "ig.release ();" + << "pg.release ();" + << "return p;" + << "}"; + } + + // find (id, obj) + // + if (id != 0) + { + string rsts (poly_derived ? "rsts" : "sts"); + + os << "bool " << traits << "::" << endl + << "find (database& db, const id_type& id, object_type& obj"; + + if (poly) + os << ", bool dyn"; + + os << ")" + << "{"; + + if (poly) + os << "ODB_POTENTIALLY_UNUSED (dyn);" + << endl; + + os << "using namespace " << db << ";" + << endl; + + if (poly) + { + if (!abst) + os << "if (dyn)" << endl + << "{"; + + os << "const std::type_info& t (typeid (obj));"; + + if (!abst) + os << endl + << "if (t != info.type)" + << "{"; + + os << "const info_type& pi (root_traits::map->find (t));" + << "return pi.dispatch (info_type::call_find, db, &obj, &id);"; + + if (!abst) + os << "}" + << "}"; + } + + if (!abst) + { + os << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << "statements_type& sts (" << endl + << "conn.statement_cache ().find_object ());"; + + if (poly_derived) + os << "root_statements_type& rsts (sts.root_statements ());"; + + // This can only be top-level call so auto_lock must succeed. + // + os << endl + << "statements_type::auto_lock l (" << rsts << ");" + << endl; + + os << "if (!find_ (sts, &id))" << endl + << "return false;" + << endl; + + os << "select_statement& st (sts.find_statement (" << + (poly_derived ? "depth" : "") << "));" + << "ODB_POTENTIALLY_UNUSED (st);" + << endl; + + if (delay_freeing_statement_result) + os << "auto_result ar (st);"; + + os << "reference_cache_traits::insert_guard ig (" << endl + << "reference_cache_traits::insert (db, id, obj));" + << endl + << "callback (db, obj, callback_event::pre_load);" + << "init (obj, sts.image (), &db);"; + + init_value_extra (); + + if (delay_freeing_statement_result) + os << "ar.free ();"; + + os << "load_ (sts, obj);" + << rsts << ".load_delayed ();" + << "l.unlock ();" + << "callback (db, obj, callback_event::post_load);" + << "ig.release ();" + << "return true;"; + } + + os << "}"; + } + + // reload () + // + if (id != 0) + { + string rsts (poly_derived ? "rsts" : "sts"); + + // This implementation is almost exactly the same as find(id, obj) + // above except that it doesn't interract with the object cache and, + // in case of optimistic concurrency, checks if the object actually + // needs to be reloaded. + // + os << "bool " << traits << "::" << endl + << "reload (database& db, object_type& obj"; + + if (poly) + os << ", bool dyn"; + + os << ")" + << "{"; + + if (poly) + os << "ODB_POTENTIALLY_UNUSED (dyn);" + << endl; + + os << "using namespace " << db << ";" + << endl; + + if (poly) + { + if (!abst) + os << "if (dyn)" << endl + << "{"; + + os << "const std::type_info& t (typeid (obj));"; + + if (!abst) + os << endl + << "if (t != info.type)" + << "{"; + + os << "const info_type& pi (root_traits::map->find (t));" + << "return pi.dispatch (info_type::call_reload, db, &obj, 0);"; + + if (!abst) + os << "}" + << "}"; + } + + if (!abst) + { + os << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << "statements_type& sts (" << endl + << "conn.statement_cache ().find_object ());"; + + if (poly_derived) + os << "root_statements_type& rsts (sts.root_statements ());"; + + // This can only be top-level call so auto_lock must succeed. + // + os << endl + << "statements_type::auto_lock l (" << rsts << ");" + << endl; + + os << "if (!find_ (sts, &obj." << id->name () << "))" << endl + << "return false;" + << endl; + + os << "select_statement& st (sts.find_statement (" << + (poly_derived ? "depth" : "") << "));" + << "ODB_POTENTIALLY_UNUSED (st);" + << endl; + + if (delay_freeing_statement_result) + os << "auto_result ar (st);" + << endl; + + if (optimistic != 0) + { + os << "if (" << (poly_derived ? "root_traits::" : "") << "version (" << + rsts << ".image ()) == obj." << optimistic->name () << ")" << endl + << "return true;" + << endl; + } + + os << "callback (db, obj, callback_event::pre_load);" + << "init (obj, sts.image (), &db);"; + + init_value_extra (); + + if (delay_freeing_statement_result) + os << "ar.free ();"; + + os << "load_ (sts, obj);" + << rsts << ".load_delayed ();" + << "l.unlock ();" + << "callback (db, obj, callback_event::post_load);" + << "return true;"; + } + + os << "}"; + } + + // find_ () + // + if (id != 0) + { + os << "bool " << traits << "::" << endl + << "find_ ("; + + if (poly && !poly_derived) + os << "base_statements_type& sts, "; + else + os << "statements_type& sts, "; + + os << "const id_type* id"; + + if (poly_derived && !abst) + os << ", std::size_t d"; + + os << ")" + << "{" + << "using namespace " << db << ";" + << endl; + + // Initialize id image. + // + if (poly_derived && !abst) + os << "if (d == depth)" + << "{"; + + os << "id_image_type& i (sts.id_image ());" + << "init (i, *id);" + << endl; + + os << "binding& idb (sts.id_image_binding ());" + << "if (i.version != sts.id_image_version () || idb.version == 0)" + << "{" + << "bind (idb.bind, i);" + << "sts.id_image_version (i.version);" + << "idb.version++;" + << "}"; + + if (poly_derived && !abst) + os << "}"; + + // Rebind data image. + // + os << "image_type& im (sts.image ());" + << "binding& imb (sts.select_image_binding (" << + (poly_derived ? (abst ? "depth" : "d") : "") << "));" + << endl; + + if (poly_derived) + { + os << "if (imb.version == 0 ||" << endl + << "check_version (sts.select_image_versions (), im))" + << "{" + << "bind (imb.bind, 0, 0, im, statement_select);" + << "update_version (sts.select_image_versions ()," << endl + << "im," << endl + << "sts.select_image_bindings ());" + << "}"; + } + else + { + os << "if (im.version != sts.select_image_version () ||" << endl + << "imb.version == 0)" + << "{" + << "bind (imb.bind, im, statement_select);" + << "sts.select_image_version (im.version);" + << "imb.version++;" + << "}"; + } + + os << "select_statement& st (sts.find_statement (" << + (poly_derived ? (abst ? "depth" : "d") : "") << "));" + << "st.execute ();" + << "auto_result ar (st);" + << "select_statement::result r (st.fetch ());" + << endl; + + if (grow) + { + os << "if (r == select_statement::truncated)" + << "{" + << "if (grow (im, sts.select_image_truncated ()" << + (poly_derived ? (abst ? ", depth" : ", d") : "") << "))" << endl + << "im.version++;" + << endl; + + if (poly_derived) + { + os << "if (check_version (sts.select_image_versions (), im))" + << "{" + << "bind (imb.bind, 0, 0, im, statement_select);" + << "update_version (sts.select_image_versions ()," << endl + << "im," << endl + << "sts.select_image_bindings ());" + << "st.refetch ();" + << "}"; + } + else + { + os << "if (im.version != sts.select_image_version ())" + << "{" + << "bind (imb.bind, im, statement_select);" + << "sts.select_image_version (im.version);" + << "imb.version++;" + << "st.refetch ();" + << "}"; + } + + os << "}"; + } + + // If we are delaying, only free the result if it is empty. + // + if (delay_freeing_statement_result) + os << "if (r != select_statement::no_data)" + << "{" + << "ar.release ();" + << "return true;" + << "}" + << "else" << endl + << "return false;"; + else + os << "return r != select_statement::no_data;"; + + os << "}"; + } + + // load_() containers + // + if (containers || poly_derived) + { + os << "void " << traits << "::" << endl + << "load_ ("; + + if (poly && !poly_derived) + os << "base_statements_type& sts, "; + else + os << "statements_type& sts, "; + + os << "object_type& obj"; + + if (poly_derived) + os << ", std::size_t d"; + + os << ")" + << "{"; + + if (poly_derived) + os << "if (--d != 0)" << endl + << "object_traits::load_ (sts.base_statements (), obj" << + (poly_base != poly_root ? ", d" : "") << ");" + << endl; + + if (containers) + { + os << db << "::binding& idb (sts.id_image_binding ());" + << endl; + instance t (container_calls::load_call); + t->traverse (c); + } + + os << "}"; + } + + // load_() + // + // Load the dynamic part of the object. We don't need it if we are + // poly-abstract. + // + if (poly_derived && !abst) + { + os << "void " << traits << "::" << endl + << "load_ (database& db, root_type& r, std::size_t d)" + << "{" + << "using namespace " << db << ";" + << endl + << "object_type& obj (static_cast (r));" + << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << "statements_type& sts (" << endl + << "conn.statement_cache ().find_object ());" + << endl + << "d = depth - d;" // Convert to distance from derived. + << endl; + + os << "if (!find_ (sts, 0, d))" << endl + << "throw object_not_persistent ();" // Database inconsistency. + << endl; + + os << "select_statement& st (sts.find_statement (d));" + << "ODB_POTENTIALLY_UNUSED (st);" + << endl; + + if (delay_freeing_statement_result) + os << "auto_result ar (st);"; + + os << "init (obj, sts.image (), &db, d);"; + + init_value_extra (); + + if (delay_freeing_statement_result) + os << "ar.free ();"; + + os << "load_ (sts, obj, d);" + << "}"; + } + + // discriminator_ () + // + if (poly && !poly_derived) + { + os << "void " << traits << "::" << endl + << "discriminator_ (statements_type & sts," << endl + << "const id_type& id," << endl + << "discriminator_type* pd"; + + if (optimistic != 0) + os << "," << endl + << "version_type* pv"; + + os << ")" + << "{" + << "using namespace " << db << ";" + << endl; + + // Initialize id image. + // + os << "id_image_type& idi (sts.discriminator_id_image ());" + << "init (idi, id);" + << endl; + + os << "binding& idb (sts.discriminator_id_image_binding ());" + << "if (idi.version != sts.discriminator_id_image_version () ||" << endl + << "idb.version == 0)" + << "{" + << "bind (idb.bind, idi" << (optimistic != 0 ? ", false" : "") << ");" + << "sts.discriminator_id_image_version (idi.version);" + << "idb.version++;" + << "}"; + + // Rebind data image. + // + os << "discriminator_image_type& i (sts.discriminator_image ());" + << "binding& imb (sts.discriminator_image_binding ());" + << endl + << "if (i.version != sts.discriminator_image_version () ||" << endl + << "imb.version == 0)" + << "{" + // Generate bind code inline. For now discriminator is simple + // value so we don't need statement kind (sk). + // + << bind_vector << " b (imb.bind);" + << "std::size_t n (0);" + << "{"; + bind_discriminator_member_->traverse (*discriminator); + os << "}"; + + if (optimistic != 0) + { + os << "n++;" // For now discriminator is a simple value. + << "{"; + bind_version_member_->traverse (*optimistic); + os << "}"; + } + + os << "sts.discriminator_image_version (i.version);" + << "imb.version++;" + << "}"; + + os << "{" + << "select_statement& st (sts.find_discriminator_statement ());" + << "st.execute ();" + << "auto_result ar (st);" + << "select_statement::result r (st.fetch ());" + << endl + << "if (r == select_statement::no_data)" + << "{"; + + if (optimistic != 0) + os << "if (pv != 0)" << endl + << "throw object_changed ();" + << "else" << endl; + + os << "throw object_not_persistent ();" + << "}"; + + if (generate_grow && ( + context::grow (*discriminator) || + (optimistic != 0 && context::grow (*optimistic)))) + { + os << "else if (r == select_statement::truncated)" + << "{"; + + // Generate grow code inline. + // + os << "bool grew (false);" + << truncated_vector << " t (sts.discriminator_image_truncated ());" + << endl; + + index_ = 0; + grow_discriminator_member_->traverse (*discriminator); + + if (optimistic != 0) + grow_version_member_->traverse (*optimistic); + + os << "if (grew)" << endl + << "i.version++;" + << endl; + + os << "if (i.version != sts.discriminator_image_version ())" + << "{" + // Generate bind code inline. The same code as above. + // + << bind_vector << " b (imb.bind);" + << "std::size_t n (0);" + << "{"; + bind_discriminator_member_->traverse (*discriminator); + os << "}"; + + if (optimistic != 0) + { + os << "n++;" // For now discriminator is a simple value. + << "{"; + bind_version_member_->traverse (*optimistic); + os << "}"; + } + + os << "sts.discriminator_image_version (i.version);" + << "imb.version++;" + << "st.refetch ();" + << "}" + << "}"; + } + + // Discriminator cannot be long data (no streaming). + // + os << "}"; + + // Initialize value inline instead of generating a separate + // init() function. For now discriminator is simple value so + // we don't need the database (db). + // + os << "if (pd != 0)" + << "{" + << "discriminator_type& d (*pd);"; + init_named_discriminator_value_member_->traverse (*discriminator); + os << "}"; + + if (optimistic != 0) + { + os << "if (pv != 0)" + << "{" + << "version_type& v (*pv);"; + init_named_version_value_member_->traverse (*optimistic); + os << "}"; + } + + os << "}"; + } + + if (options.generate_query ()) + { + // query () + // + os << "result< " << traits << "::object_type >" << endl + << traits << "::" << endl + << "query (database&, const query_base_type& q)" + << "{" + << "using namespace " << db << ";" + << "using odb::details::shared;" + << "using odb::details::shared_ptr;" + << endl + << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << endl + << "statements_type& sts (" << endl + << "conn.statement_cache ().find_object ());" + << endl; + + // Rebind the image if necessary. + // + os << "image_type& im (sts.image ());" + << "binding& imb (sts.select_image_binding (" << + (poly_derived ? "depth" : "") << "));" + << endl; + + if (poly_derived) + { + os << "if (imb.version == 0 ||" << endl + << "check_version (sts.select_image_versions (), im))" + << "{" + << "bind (imb.bind, 0, 0, im, statement_select);" + << "update_version (sts.select_image_versions ()," << endl + << "im," << endl + << "sts.select_image_bindings ());" + << "}"; + } + else + { + os << "if (im.version != sts.select_image_version () ||" << endl + << "imb.version == 0)" + << "{" + << "bind (imb.bind, im, statement_select);" + << "sts.select_image_version (im.version);" + << "imb.version++;" + << "}"; + } + + os << "shared_ptr st (" << endl + << "new (shared) select_statement (" << endl; + object_query_statement_ctor_args (c); + os << "));" << endl + << "st->execute ();"; + + post_query_ (c); + + char const* result_type; + if (poly) + result_type = "polymorphic_object_result_impl"; + else if (id != 0) + result_type = "object_result_impl"; + else + result_type = "no_id_object_result_impl"; + + os << endl + << "shared_ptr< odb::" << result_type << " > r (" << endl + << "new (shared) " << db << "::" << result_type << " (" << endl + << "q, st, sts));" + << endl + << "return result (r);" + << "}"; + + // erase_query + // + os << "unsigned long long " << traits << "::" << endl + << "erase_query (database&, const query_base_type& q)" + << "{" + << "using namespace " << db << ";" + << endl + << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << endl + << "delete_statement st (" << endl; + object_erase_query_statement_ctor_args (c); + os << ");" + << endl + << "return st.execute ();" + << "}"; + } + + if (embedded_schema) + schema_->traverse (c); +} + +void relational::source::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; + + view_extra (c); + + // + // Functions. + // + + // grow () + // + if (generate_grow) + { + os << "bool " << traits << "::" << endl + << "grow (image_type& i, " << truncated_vector << " t)" + << "{" + << "ODB_POTENTIALLY_UNUSED (i);" + << "ODB_POTENTIALLY_UNUSED (t);" + << endl + << "bool grew (false);" + << endl; + + index_ = 0; + names (c, grow_member_names_); + + os << "return grew;" + << "}"; + } + + // bind (image_type) + // + os << "void " << traits << "::" << endl + << "bind (" << bind_vector << " b, image_type& i)" + << "{" + << "using namespace " << db << ";" + << endl + << db << "::statement_kind sk (statement_select);" + << "ODB_POTENTIALLY_UNUSED (sk);" + << endl + << "std::size_t n (0);" + << endl; + + names (c, bind_member_names_); + + os << "}"; + + // init (view, image) + // + os << "void " << traits << "::" << endl + << "init (view_type& o, const image_type& i, database* db)" + << "{" + << "ODB_POTENTIALLY_UNUSED (o);" + << "ODB_POTENTIALLY_UNUSED (i);" + << "ODB_POTENTIALLY_UNUSED (db);" + << endl; + + names (c, init_value_member_names_); + + os << "}"; + + // query_statement() + // + view_query& vq (c.get ("query")); + + if (vq.kind != view_query::runtime) + { + os << traits << "::query_base_type" << endl + << traits << "::" << endl + << "query_statement (const query_base_type& q)" + << "{"; + + if (vq.kind == view_query::complete) + { + os << "query_base_type r (" << endl; + + bool ph (false); + + if (!vq.literal.empty ()) + { + // See if we have the '(?)' placeholder. + // + // @@ Ideally we would need to make sure we don't match + // this inside strings and quoted identifier. So the + // proper way to handle this would be to tokenize the + // statement using sql_lexer, once it is complete enough. + // + string::size_type p (vq.literal.find ("(?)")); + + if (p != string::npos) + { + ph = true; + os << strlit (string (vq.literal, 0, p + 1)) << " +" << endl + << "(q.empty () ? query_base_type::true_expr : q) +" << endl + << strlit (string (vq.literal, p + 2)); + } + else + os << strlit (vq.literal); + } + else + // Output the pragma location for easier error tracking. + // + os << "// From " << + location_file (vq.loc).leaf () << ":" << + location_line (vq.loc) << ":" << + location_column (vq.loc) << endl + << translate_expression ( + c, vq.expr, vq.scope, vq.loc, "query", &ph).value; + + os << ");"; + + // If there was no placeholder, add the query condition + // at the end. + // + if (!ph) + os << "r += q.clause_prefix ();" + << "r += q;"; + } + else // vq.kind == view_query::condition + { + statement_columns sc; + { + instance t (sc); + t->traverse (c); + process_statement_columns (sc, statement_select); + } + + os << "query_base_type r (" << endl + << strlit ("SELECT ") << endl; + + for (statement_columns::const_iterator i (sc.begin ()), + e (sc.end ()); i != e;) + { + string const& c (i->column); + os << strlit (c + (++i != e ? "," : "")) << endl; + } + + os << ");" + << endl; + + // Generate from-list. + // + view_objects const& objs (c.get ("objects")); + + for (view_objects::const_iterator i (objs.begin ()); + i != objs.end (); + ++i) + { + bool first (i == objs.begin ()); + string l; + + // + // Tables. + // + + if (i->kind == view_object::table) + { + if (first) + { + l = "FROM "; + l += quote_id (i->tbl_name); + + if (!i->alias.empty ()) + l += (need_alias_as ? " AS " : " ") + quote_id (i->alias); + + os << "r += " << strlit (l) << ";" + << endl; + + continue; + } + + l = "LEFT JOIN "; + l += quote_id (i->tbl_name); + + if (!i->alias.empty ()) + l += (need_alias_as ? " AS " : " ") + quote_id (i->alias); + + expression e ( + translate_expression ( + c, i->cond, i->scope, i->loc, "table")); + + if (e.kind != expression::literal) + { + error (i->loc) + << "invalid join condition in db pragma table" << endl; + + throw operation_failed (); + } + + l += " ON"; + + os << "r += " << strlit (l) << ";" + // Output the pragma location for easier error tracking. + // + << "// From " << + location_file (i->loc).leaf () << ":" << + location_line (i->loc) << ":" << + location_column (i->loc) << endl + << "r += " << e.value << ";" + << endl; + + continue; + } + + // + // Objects. + // + semantics::class_& o (*i->obj); + + bool poly (polymorphic (o)); + size_t poly_depth (poly ? polymorphic_depth (o) : 1); + + string alias (i->alias); + + // For polymorphic objects, alias is just a prefix. + // + if (poly && !alias.empty ()) + alias += "_" + table_name (o).uname (); + + // First object. + // + if (first) + { + l = "FROM "; + l += table_qname (o); + + if (!alias.empty ()) + l += (need_alias_as ? " AS " : " ") + quote_id (alias); + + os << "r += " << strlit (l) << ";"; + + if (poly_depth != 1) + { + instance j ( + o, poly_depth, i->alias, "r += ", ";"); + j->traverse (o); + } + + os << endl; + + continue; + } + + expression e ( + translate_expression ( + c, i->cond, i->scope, i->loc, "object")); + + // Literal expression. + // + if (e.kind == expression::literal) + { + l = "LEFT JOIN "; + l += table_qname (o); + + if (!alias.empty ()) + l += (need_alias_as ? " AS " : " ") + quote_id (alias); + + l += " ON"; + + os << "r += " << strlit (l) << ";" + // Output the pragma location for easier error tracking. + // + << "// From " << + location_file (i->loc).leaf () << ":" << + location_line (i->loc) << ":" << + location_column (i->loc) << endl + << "r += " << e.value << ";"; + + if (poly_depth != 1) + { + instance j ( + o, poly_depth, i->alias, "r += ", ";"); + j->traverse (o); + } + + os << endl; + + continue; + } + + // We have an object relationship (pointer) for which we need + // to come up with the corresponding JOIN condition. If this + // is a to-many relationship, then we first need to JOIN the + // container table. This code is similar to object_joins. + // + using semantics::data_member; + + data_member& m (*e.member_path.back ()); + + // Resolve the pointed-to object to view_object and do + // some sanity checks while at it. + // + semantics::class_* c (0); + + if (semantics::type* cont = container (m)) + c = object_pointer (container_vt (*cont)); + else + c = object_pointer (utype (m)); + + view_object const* vo (0); + + // Check if the pointed-to object has been previously + // associated with this view and is unambiguous. A + // pointer to ourselves is always assumed to point + // to this association. + // + if (&o == c) + vo = &*i; + else + { + bool ambig (false); + + for (view_objects::const_iterator j (objs.begin ()); + j != i; + ++j) + { + if (j->obj != c) + continue; + + if (vo == 0) + { + vo = &*j; + continue; + } + + // If it is the first ambiguous object, issue the + // error. + // + if (!ambig) + { + error (i->loc) + << "pointed-to object '" << class_name (*c) << "' is " + << "ambiguous" << endl; + + info (i->loc) + << "candidates are:" << endl; + + info (vo->loc) + << " '" << vo->name () << "'" << endl; + + ambig = true; + } + + info (j->loc) + << " '" << j->name () << "'" << endl; + } + + if (ambig) + { + info (i->loc) + << "use the full join condition clause in db pragma " + << "object to resolve this ambiguity" << endl; + + throw operation_failed (); + } + + if (vo == 0) + { + error (i->loc) + << "pointed-to object '" << class_name (*c) << "' " + << "specified in the join condition has not been " + << "previously associated with this view" << endl; + + throw operation_failed (); + } + } + + // Left and right-hand side table names. + // + qname lt; + { + using semantics::class_; + + class_& o (*e.vo->obj); + string const& a (e.vo->alias); + + if (class_* root = polymorphic (o)) + { + // If the object is polymorphic, then figure out which of the + // bases this member comes from and use the corresponding + // table. + // + class_* c ( + &static_cast ( + e.member_path.front ()->scope ())); + + // If this member's class is not polymorphic (root uses reuse + // inheritance), then use the root table. + // + if (!polymorphic (*c)) + c = root; + + qname const& t (table_name (*c)); + + if (a.empty ()) + lt = t; + else + lt = qname (a + "_" + t.uname ()); + } + else + lt = a.empty () ? table_name (o) : qname (a); + } + + qname rt; + { + qname t (table_name (*vo->obj)); + string const& a (vo->alias); + rt = a.empty () + ? t + : qname (polymorphic (*vo->obj) ? a + "_" + t.uname () : a); + } + + // First join the container table if necessary. + // + data_member* im (inverse (m)); + + semantics::type* cont (container (im != 0 ? *im : m)); + + string ct; // Container table. + if (cont != 0) + { + if (im != 0) + { + // For now a direct member can only be directly in + // the object scope. If this changes, the inverse() + // function would have to return a member path instead + // of just a single member. + // + table_prefix tp ( + context::schema (vo->obj->scope ()), + context::table_name_prefix (vo->obj->scope ()), + table_name (*vo->obj) + "_"); + ct = table_qname (*im, tp); + } + else + ct = table_qname (*e.vo->obj, e.member_path); + + l = "LEFT JOIN "; + l += ct; + l += " ON"; + os << "r += " << strlit (l) << ";"; + + // If we are the pointed-to object, then we have to turn + // things around. This is necessary to have the proper + // JOIN order. There seems to be a pattern there but it + // is not yet intuitively clear what it means. + // + instance c_cols; // Container columns. + instance o_cols; // Object columns. + + qname* ot; // Object table (either lt or rt). + + if (im != 0) + { + if (&o == c) + { + // container.value = pointer.id + // + semantics::data_member& id (*id_member (*e.vo->obj)); + + c_cols->traverse (*im, utype (id), "value", "value"); + o_cols->traverse (id); + ot = < + } + else + { + // container.id = pointed-to.id + // + semantics::data_member& id (*id_member (*vo->obj)); + + c_cols->traverse ( + *im, utype (id), "id", "object_id", vo->obj); + o_cols->traverse (id); + ot = &rt; + } + } + else + { + if (&o == c) + { + // container.id = pointer.id + // + semantics::data_member& id (*id_member (*e.vo->obj)); + + c_cols->traverse ( + m, utype (id), "id", "object_id", e.vo->obj); + o_cols->traverse (id); + ot = < + } + else + { + // container.value = pointed-to.id + // + semantics::data_member& id (*id_member (*vo->obj)); + + c_cols->traverse (m, utype (id), "value", "value"); + o_cols->traverse (id); + ot = &rt; + } + } + + for (object_columns_list::iterator b (c_cols->begin ()), i (b), + j (o_cols->begin ()); i != c_cols->end (); ++i, ++j) + { + l.clear (); + + if (i != b) + l += "AND "; + + l += ct; + l += '.'; + l += quote_id (i->name); + l += '='; + l += quote_id (*ot); + l += '.'; + l += quote_id (j->name); + + os << "r += " << strlit (l) << ";"; + } + } + + l = "LEFT JOIN "; + l += table_qname (o); + + if (!alias.empty ()) + l += (need_alias_as ? " AS " : " ") + quote_id (alias); + + l += " ON"; + os << "r += " << strlit (l) << ";"; + + if (cont != 0) + { + instance c_cols; // Container columns. + instance o_cols; // Object columns. + + qname* ot; // Object table (either lt or rt). + + if (im != 0) + { + if (&o == c) + { + // container.id = pointed-to.id + // + semantics::data_member& id (*id_member (*vo->obj)); + + c_cols->traverse (*im, utype (id), "id", "object_id", vo->obj); + o_cols->traverse (id); + ot = &rt; + } + else + { + // container.value = pointer.id + // + semantics::data_member& id (*id_member (*e.vo->obj)); + + c_cols->traverse (*im, utype (id), "value", "value"); + o_cols->traverse (id); + ot = < + } + } + else + { + if (&o == c) + { + // container.value = pointed-to.id + // + semantics::data_member& id (*id_member (*vo->obj)); + + c_cols->traverse (m, utype (id), "value", "value"); + o_cols->traverse (id); + ot = &rt; + } + else + { + // container.id = pointer.id + // + semantics::data_member& id (*id_member (*e.vo->obj)); + + c_cols->traverse (m, utype (id), "id", "object_id", e.vo->obj); + o_cols->traverse (id); + ot = < + } + } + + for (object_columns_list::iterator b (c_cols->begin ()), i (b), + j (o_cols->begin ()); i != c_cols->end (); ++i, ++j) + { + l.clear (); + + if (i != b) + l += "AND "; + + l += ct; + l += '.'; + l += quote_id (i->name); + l += '='; + l += quote_id (*ot); + l += '.'; + l += quote_id (j->name); + + os << "r += " << strlit (l) << ";"; + } + } + else + { + string col_prefix; + + if (im == 0) + col_prefix = object_columns_base::column_prefix (e.member_path); + + instance l_cols (col_prefix); + instance r_cols; + + if (im != 0) + { + // our.id = pointed-to.pointer + // + l_cols->traverse (*id_member (*e.vo->obj)); + r_cols->traverse (*im); + } + else + { + // our.pointer = pointed-to.id + // + l_cols->traverse (*e.member_path.back ()); + r_cols->traverse (*id_member (*vo->obj)); + } + + for (object_columns_list::iterator b (l_cols->begin ()), i (b), + j (r_cols->begin ()); i != l_cols->end (); ++i, ++j) + { + l.clear (); + + if (i != b) + l += "AND "; + + l += quote_id (lt); + l += '.'; + l += quote_id (i->name); + l += '='; + l += quote_id (rt); + l += '.'; + l += quote_id (j->name); + + os << "r += " << strlit (l) << ";"; + } + } + + if (poly_depth != 1) + { + instance j ( + o, poly_depth, i->alias, "r += ", ";"); + j->traverse (o); + } + + os << endl; + + } // End JOIN-generating for-loop. + + // Generate the query condition. + // + if (!vq.literal.empty () || !vq.expr.empty ()) + { + os << "query_base_type c (" << endl; + + bool ph (false); + + if (!vq.literal.empty ()) + { + // See if we have the '(?)' placeholder. + // + // @@ Ideally we would need to make sure we don't match + // this inside strings and quoted identifier. So the + // proper way to handle this would be to tokenize the + // statement using sql_lexer, once it is complete enough. + // + string::size_type p (vq.literal.find ("(?)")); + + if (p != string::npos) + { + ph = true; + os << strlit (string (vq.literal, 0, p + 1))<< " +" << endl + << "(q.empty () ? query_base_type::true_expr : q) +" << endl + << strlit (string (vq.literal, p + 2)); + } + else + os << strlit (vq.literal); + + os << ");"; + } + else + { + // Output the pragma location for easier error tracking. + // + os << "// From " << + location_file (vq.loc).leaf () << ":" << + location_line (vq.loc) << ":" << + location_column (vq.loc) << endl + << translate_expression ( + c, vq.expr, vq.scope, vq.loc, "query", &ph).value; + + os << ");"; + + // Optimize the query if it had a placeholder. This gets + // rid of useless clauses like WHERE TRUE. + // + if (ph) + os << "c.optimize ();"; + } + + if (!ph) + os << "c += q;"; + + os << "r += c.clause_prefix ();" + << "r += c;" + << endl; + } + else + { + os << "r += q.clause_prefix ();" + << "r += q;" + << endl; + } + } + + os << "return r;" + << "}"; + } + + // query () + // + os << "result< " << traits << "::view_type >" << endl + << traits << "::" << endl + << "query (database&, const query_base_type& q)" + << "{" + << "using namespace " << db << ";" + << "using odb::details::shared;" + << "using odb::details::shared_ptr;" + << endl + << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << endl + << "view_statements< view_type >& sts (" << endl + << "conn.statement_cache ().find_view ());" + << endl + << "image_type& im (sts.image ());" + << "binding& imb (sts.image_binding ());" + << endl + << "if (im.version != sts.image_version () || imb.version == 0)" + << "{" + << "bind (imb.bind, im);" + << "sts.image_version (im.version);" + << "imb.version++;" + << "}"; + + if (vq.kind == view_query::runtime) + os << "const query_base_type& qs (q);"; + else + os << "const query_base_type& qs (query_statement (q));"; + + os << "shared_ptr st (" << endl + << "new (shared) select_statement (" << endl; + + view_query_statement_ctor_args (c); + + os << "));" << endl + << "st->execute ();"; + + post_query_ (c); + + os << endl + << "shared_ptr< odb::view_result_impl > r (" << endl + << "new (shared) " << db << "::view_result_impl (" << endl + << "qs, st, sts));" + << endl + << "return result (r);" + << "}"; +} + namespace relational { namespace source diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx index e6ca961..42ff235 100644 --- a/odb/relational/source.hxx +++ b/odb/relational/source.hxx @@ -26,15 +26,17 @@ namespace relational struct statement_column { statement_column (): member (0) {} - statement_column (std::string const& c, + statement_column (std::string const& tbl, + std::string const& col, std::string const& t, semantics::data_member& m, std::string const& kp = "") - : column (c), type (t), member (&m), key_prefix (kp) + : table (tbl), column (col), type (t), member (&m), key_prefix (kp) { } - std::string column; // Column name. + std::string table; // Schema-qualifed and quoted table name. + std::string column; // Table-qualifed and quoted column expr. std::string type; // Column SQL type. semantics::data_member* member; std::string key_prefix; @@ -73,20 +75,55 @@ namespace relational object_columns (statement_kind sk, statement_columns& sc, query_parameters* param = 0) - : sk_ (sk), sc_ (sc), param_ (param) + : object_columns_base (true, "", true), + sk_ (sk), sc_ (sc), param_ (param), depth_ (1) { } object_columns (std::string const& table_qname, statement_kind sk, - statement_columns& sc) - : sk_ (sk), sc_ (sc), param_ (0), table_name_ (table_qname) + statement_columns& sc, + size_t depth = 1) + : object_columns_base (true, "", true), + sk_ (sk), + sc_ (sc), + param_ (0), + table_name_ (table_qname), + depth_ (depth) + { + } + + virtual void + traverse_object (semantics::class_& c) { + // If we are generating a select statement and this is a derived + // type in a polymorphic hierarchy, then we need to include base + // columns, but do it in reverse order as well as switch the table + // name (base columns come from different tables). + // + semantics::class_* poly_root (polymorphic (c)); + if (poly_root != 0 && poly_root != &c) + { + names (c); + + if (sk_ == statement_select && --depth_ != 0) + { + table_name_ = table_qname (polymorphic_base (c)); + inherits (c); + } + } + else + object_columns_base::traverse_object (c); } virtual void traverse_pointer (semantics::data_member& m, semantics::class_& c) { + // Ignore polymorphic id references for select statements. + // + if (sk_ == statement_select && m.count ("polymorphic-ref")) + return; + semantics::data_member* im (inverse (m, key_prefix_)); // Ignore certain columns depending on what kind statement we are @@ -215,7 +252,9 @@ namespace relational r += param_->next (); } - sc_.push_back (statement_column (r, column_type (), m, key_prefix_)); + sc_.push_back ( + statement_column ( + table, r, column_type (), m, key_prefix_)); } protected: @@ -223,6 +262,7 @@ namespace relational statement_columns& sc_; query_parameters* param_; string table_name_; + size_t depth_; }; struct view_columns: object_columns_base, virtual context @@ -306,6 +346,7 @@ namespace relational virtual bool traverse_column (semantics::data_member& m, string const& name, bool) { + string tbl; string col; // If we are inside a composite value, use the standard @@ -315,7 +356,8 @@ namespace relational { if (!table_prefix_.empty ()) { - col += quote_id (table_prefix_); + tbl = quote_id (table_prefix_); + col += tbl; col += '.'; } @@ -331,7 +373,8 @@ namespace relational { if (!tc.table.empty ()) { - col += quote_id (tc.table); + tbl = quote_id (tc.table); + col += tbl; col += '.'; } @@ -357,7 +400,8 @@ namespace relational } case column_expr_part::reference: { - col += quote_id (i->table); + tbl = quote_id (i->table); + col += tbl; col += '.'; col += quote_id (column_name (i->member_path)); break; @@ -378,7 +422,7 @@ namespace relational throw operation_failed (); } - column (m, col); + column (m, tbl, col); return true; } @@ -386,9 +430,11 @@ namespace relational // expression. // virtual void - column (semantics::data_member& m, string const& column) + column (semantics::data_member& m, + string const& table, + string const& column) { - sc_.push_back (statement_column (column, column_type (), m)); + sc_.push_back (statement_column (table, column, column_type (), m)); } protected: @@ -397,50 +443,132 @@ namespace relational qname table_prefix_; // Table corresponding to column_prefix_; }; + struct polymorphic_object_joins: object_columns_base, virtual context + { + typedef polymorphic_object_joins base; + + polymorphic_object_joins (semantics::class_& obj, + size_t depth, + string const& alias = "", + string const prefix = "", + string const& suffix = "\n") + : object_columns_base (true, "", true), + obj_ (obj), + depth_ (depth), + alias_ (alias), + prefix_ (prefix), + suffix_ (suffix) + { + } + + virtual void + traverse_object (semantics::class_& c) + { + if (&c == &obj_) + { + // Get the table and id columns. + // + table_ = alias_.empty () + ? table_qname (c) + : quote_id (alias_ + "_" + table_name (c).uname ()); + + cols_->traverse (*id_member (c)); + + if (--depth_ != 0) + inherits (c); + return; + } + + semantics::class_* poly_root (); + std::ostringstream cond; + + qname table (table_name (c)); + string alias (alias_.empty () + ? quote_id (table) + : quote_id (alias_ + "_" + table.uname ())); + + for (object_columns_list::iterator b (cols_->begin ()), i (b); + i != cols_->end (); + ++i) + { + if (i != b) + cond << " AND "; + + string qn (quote_id (i->name)); + cond << alias << '.' << qn << '=' << table_ << '.' << qn; + } + + string line (" LEFT JOIN " + quote_id (table)); + + if (!alias_.empty ()) + line += (need_alias_as ? " AS " : " ") + alias; + + line += " ON " + cond.str (); + + os << prefix_ << strlit (line) << suffix_; + + if (&c != polymorphic (c) && --depth_ != 0) + inherits (c); + } + + private: + semantics::class_& obj_; + size_t depth_; + string alias_; + string prefix_; + string suffix_; + string table_; + instance cols_; + }; + struct object_joins: object_columns_base, virtual context { typedef object_joins base; //@@ context::{cur,top}_object; might have to be created every time. // - object_joins (semantics::class_& scope, bool query) - : query_ (query), + object_joins (semantics::class_& scope, bool query, size_t depth = 1) + : object_columns_base (true, "", true), + query_ (query), + depth_ (depth), table_ (table_qname (scope)), id_ (*id_member (scope)) { id_cols_->traverse (id_); } - size_t - count () const - { - return joins_.size (); - } - - void - write () + virtual void + traverse_object (semantics::class_& c) { - for (joins::iterator i (joins_.begin ()); i != joins_.end (); ++i) + // If this is a derived type in a polymorphic hierarchy, then we + // need to include base joins, but do it in reverse order as well + // as switch the table name (base columns come from different + // tables). + // + semantics::class_* poly_root (polymorphic (c)); + if (poly_root != 0 && poly_root != &c) { - if (i->table.empty ()) - continue; - - string line (" LEFT JOIN "); - line += i->table; + names (c); - if (!i->alias.empty ()) - line += (need_alias_as ? " AS " : " ") + i->alias; - - line += " ON "; - line += i->cond; - - os << strlit (line) << endl; + if (query_ || --depth_ != 0) + { + table_ = table_qname (polymorphic_base (c)); + inherits (c); + } } + else + object_columns_base::traverse_object (c); } virtual void traverse_pointer (semantics::data_member& m, semantics::class_& c) { + // Ignore polymorphic id references; they are joined by + // polymorphic_object_joins in a special way. + // + if (m.count ("polymorphic-ref")) + return; + string t, a, dt, da; std::ostringstream cond, dcond; // @@ diversion? @@ -466,8 +594,13 @@ namespace relational alias = column_prefix_ + p; } else - alias = column_prefix_ + - column_name (m, key_prefix_, default_name_); + alias = column_prefix_ + column_name (m, key_prefix_, default_name_); + + semantics::class_* poly_root (polymorphic (c)); + bool poly (poly_root != 0); + bool poly_derived (poly && poly_root != &c); + + bool obj_joined (false); if (semantics::data_member* im = inverse (m, key_prefix_)) { @@ -503,8 +636,10 @@ namespace relational // if (query_) { - dt = quote_id (ct); - da = quote_id (alias); + qname const& table (table_name (c)); + + dt = quote_id (table); + da = quote_id (poly ? alias + "_" + table.uname () : alias); semantics::data_member& id (*id_member (c)); @@ -522,12 +657,16 @@ namespace relational dcond << da << '.' << quote_id (j->name) << '=' << t << '.' << quote_id (i->name); } + + obj_joined = true; } } else { - t = table_qname (c); - a = quote_id (alias); + qname const& table (table_name (c)); + + t = quote_id (table); + a = quote_id (poly ? alias + "_" + table.uname () : alias); instance id_cols; id_cols->traverse (*im); @@ -542,6 +681,8 @@ namespace relational cond << a << '.' << quote_id (i->name) << '=' << table_ << '.' << quote_id (j->name); } + + obj_joined = true; } } else if (query_) @@ -549,8 +690,10 @@ namespace relational // We need the join to be able to use the referenced object // in the WHERE clause. // - t = table_qname (c); - a = quote_id (alias); + qname const& table (table_name (c)); + + t = quote_id (table); + a = quote_id (poly ? alias + "_" + table.uname () : alias); instance oid_cols (column_prefix_); oid_cols->traverse (m); @@ -568,14 +711,22 @@ namespace relational cond << a << '.' << quote_id (i->name) << '=' << table_ << '.' << quote_id (j->name); } + + obj_joined = true; } if (!t.empty ()) { - joins_.push_back (join ()); - joins_.back ().table = t; - joins_.back ().alias = a; - joins_.back ().cond = cond.str (); + string line (" LEFT JOIN "); + line += t; + + if (!a.empty ()) + line += (need_alias_as ? " AS " : " ") + a; + + line += " ON "; + line += cond.str (); + + os << strlit (line) << endl; } // Add dependent join (i.e., an object table join via the @@ -583,29 +734,35 @@ namespace relational // if (!dt.empty ()) { - joins_.push_back (join ()); - joins_.back ().table = dt; - joins_.back ().alias = da; - joins_.back ().cond = dcond.str (); + string line (" LEFT JOIN "); + line += dt; + + if (!da.empty ()) + line += (need_alias_as ? " AS " : " ") + da; + + line += " ON "; + line += dcond.str (); + + os << strlit (line) << endl; + } + + // If we joined the object and it is a derived type in a + // polymorphic hierarchy, then join its bases as well. + // + if (obj_joined && poly_derived) + { + size_t depth (polymorphic_depth (c)); + instance t (c, depth, alias); + t->traverse (c); } } private: bool query_; + size_t depth_; string table_; semantics::data_member& id_; instance id_cols_; - - struct join - { - string table; - string alias; - string cond; - }; - - typedef std::vector joins; - - joins joins_; }; // @@ -637,6 +794,146 @@ namespace relational string arg_override_; }; + template + struct bind_member_impl: bind_member, virtual member_base_impl + { + typedef bind_member_impl base_impl; + + bind_member_impl (base const& x) + : base (x) + { + } + + typedef typename member_base_impl::member_info member_info; + + virtual bool + pre (member_info& mi) + { + if (container (mi)) + return false; + + // Ignore polymorphic id references; they are bound in a special + // way. + // + if (mi.ptr != 0 && mi.m.count ("polymorphic-ref")) + return false; + + std::ostringstream ostr; + ostr << "b[n]"; + b = ostr.str (); + + arg = arg_override_.empty () ? string ("i") : arg_override_; + + if (var_override_.empty ()) + { + os << "// " << mi.m.name () << endl + << "//" << endl; + + if (!insert_send_auto_id && id (mi.m) && auto_ (mi.m)) + os << "if (sk != statement_insert && sk != statement_update)" + << "{"; + else if (inverse (mi.m, key_prefix_) || version (mi.m)) + os << "if (sk == statement_select)" + << "{"; + // If the whole class is readonly, then we will never be + // called with sk == statement_update. + // + else if (!readonly (*context::top_object)) + { + semantics::class_* c; + + if (id (mi.m) || + readonly (mi.m) || + ((c = composite (mi.t)) && readonly (*c))) + os << "if (sk != statement_update)" + << "{"; + } + } + + return true; + } + + virtual void + post (member_info& mi) + { + if (var_override_.empty ()) + { + semantics::class_* c; + + if ((c = composite (mi.t))) + { + bool ro (readonly (*c)); + column_count_type const& cc (column_count (*c)); + + os << "n += " << cc.total << "UL"; + + // select = total + // insert = total - inverse + // update = total - inverse - readonly + // + if (cc.inverse != 0 || (!ro && cc.readonly != 0)) + { + os << " - (" << endl + << "sk == statement_select ? 0 : "; + + if (cc.inverse != 0) + os << cc.inverse << "UL"; + + if (!ro && cc.readonly != 0) + { + if (cc.inverse != 0) + os << " + "; + + os << "(" << endl + << "sk == statement_insert ? 0 : " << + cc.readonly << "UL)"; + } + + os << ")"; + } + + os << ";"; + } + else + os << "n++;"; + + bool block (false); + + // The same logic as in pre(). + // + if (!insert_send_auto_id && id (mi.m) && auto_ (mi.m)) + block = true; + else if (inverse (mi.m, key_prefix_) || version (mi.m)) + block = true; + else if (!readonly (*context::top_object)) + { + semantics::class_* c; + + if (id (mi.m) || + readonly (mi.m) || + ((c = composite (mi.t)) && readonly (*c))) + block = true; + } + + if (block) + os << "}"; + else + os << endl; + } + } + + virtual void + traverse_composite (member_info& mi) + { + os << "composite_value_traits< " << mi.fq_type () << + " >::bind (b + n, " << arg << "." << mi.var << "value, sk);"; + } + + protected: + string b; + string arg; + }; + struct bind_base: traversal::class_, virtual context { typedef bind_base base; @@ -721,8 +1018,8 @@ namespace relational { typedef grow_member base; - grow_member (size_t& index) - : member_base (string (), 0, string (), string ()), index_ (index) + grow_member (size_t& index, string const& var = string ()) + : member_base (var, 0, string (), string ()), index_ (index) { } @@ -827,6 +1124,12 @@ namespace relational if (container (mi) || inverse (mi.m, key_prefix_)) return false; + // Ignore polymorphic id references; they are initialized in a + // special way. + // + if (mi.ptr != 0 && mi.m.count ("polymorphic-ref")) + return false; + if (!member_override_.empty ()) member = member_override_; else @@ -844,7 +1147,6 @@ namespace relational return false; string const& name (mi.m.name ()); - member = "o." + name; os << "// " << name << endl << "//" << endl; @@ -861,8 +1163,19 @@ namespace relational ((c = composite (mi.t)) && readonly (*c))) // Can't be id. os << "if (sk == statement_insert)"; } + + if (discriminator (mi.m)) + member = "di.discriminator"; + else + member = "o." + name; } + os << "{"; + + if (discriminator (mi.m)) + os << "const info_type& di (map->find (typeid (o)));" + << endl; + bool comp (composite (mi.t)); // If this is a wrapped composite value, then we need to "unwrap" @@ -888,15 +1201,14 @@ namespace relational // Handle NULL pointers and extract the id. // - os << "{" - << "typedef object_traits< " << class_fq_name (*mi.ptr) << + os << "typedef object_traits< " << class_fq_name (*mi.ptr) << " > obj_traits;"; if (weak_pointer (pt)) { - os << "typedef pointer_traits< " << mi.ptr_fq_type () << + os << "typedef odb::pointer_traits< " << mi.ptr_fq_type () << " > wptr_traits;" - << "typedef pointer_traits< wptr_traits::" << + << "typedef odb::pointer_traits< wptr_traits::" << "strong_pointer_type > ptr_traits;" << endl << "wptr_traits::strong_pointer_type sp (" << @@ -905,7 +1217,7 @@ namespace relational member = "sp"; } else - os << "typedef pointer_traits< " << mi.ptr_fq_type () << + os << "typedef odb::pointer_traits< " << mi.ptr_fq_type () << " > ptr_traits;" << endl; @@ -926,17 +1238,11 @@ namespace relational member = "id"; } else if (comp) - { type = mi.fq_type (); - - os << "{"; - } else { type = mi.fq_type (); - - os << "{" - << "bool is_null;"; + os << "bool is_null;"; } if (comp) @@ -1045,9 +1351,12 @@ namespace relational { typedef init_value_member base; - init_value_member (string const& member = string ()) - : member_base (string (), 0, string (), string ()), - member_override_ (member) + init_value_member (string const& member = string (), + string const& var = string (), + bool ignore_implicit_discriminator = true) + : member_base (var, 0, string (), string ()), + member_override_ (member), + ignore_implicit_discriminator_ (ignore_implicit_discriminator) { } @@ -1057,12 +1366,14 @@ namespace relational string const& fq_type, string const& key_prefix) : member_base (var, &t, fq_type, key_prefix), - member_override_ (member) + member_override_ (member), + ignore_implicit_discriminator_ (true) { } protected: string member_override_; + bool ignore_implicit_discriminator_; }; template @@ -1090,6 +1401,17 @@ namespace relational if (container (mi)) return false; + // Ignore polymorphic id references; they are initialized in a + // special way. + // + if (mi.ptr != 0 && mi.m.count ("polymorphic-ref")) + return false; + + // Ignore implicit discriminators. + // + if (ignore_implicit_discriminator_ && discriminator (mi.m)) + return false; + if (!member_override_.empty ()) member = member_override_; else @@ -1130,7 +1452,7 @@ namespace relational os << "{" << "typedef object_traits< " << class_fq_name (*mi.ptr) << " > obj_traits;" - << "typedef pointer_traits< " << mi.ptr_fq_type () << + << "typedef odb::pointer_traits< " << mi.ptr_fq_type () << " > ptr_traits;" << endl; @@ -1213,8 +1535,8 @@ namespace relational if (weak_pointer (pt)) { os << endl - << "if (pointer_traits< ptr_traits::strong_pointer_type >" << - "::null_ptr (" << endl + << "if (odb::pointer_traits<" << + "ptr_traits::strong_pointer_type>::null_ptr (" << endl << "ptr_traits::lock (" << member << ")))" << endl << "throw session_required ();"; } @@ -1339,20 +1661,20 @@ namespace relational using semantics::type; // Figure out if this member is from a base object or composite - // value and whether it is abstract. + // value and if it's from an object, whether it is reuse-abstract. // - bool base, abst; + bool base, reuse_abst; if (object (c_)) { base = cur_object != &c_ || !object (dynamic_cast (m.scope ())); - abst = abstract (c_); + reuse_abst = abstract (c_) && !polymorphic (c_); } else { - base = false; // We don't go into bases. - abst = true; // Always abstract. + base = false; // We don't go into bases. + reuse_abst = true; // Always abstract. } container_kind_type ck (container_kind (t)); @@ -1425,7 +1747,7 @@ namespace relational // // Statements. // - if (!abst) + if (!reuse_abst) { semantics::type& idt (container_idt (m)); @@ -1434,8 +1756,8 @@ namespace relational // select_all_statement // - os << "const char " << scope << - "::select_all_statement[] =" << endl; + os << "const char " << scope << "::" << endl + << "select_all_statement[] =" << endl; if (inverse) { @@ -1472,6 +1794,7 @@ namespace relational // sc.push_back ( statement_column ( + inv_table, inv_table + "." + quote_id (i->name), i->type, *i->member, @@ -1492,6 +1815,7 @@ namespace relational { sc.push_back ( statement_column ( + inv_table, inv_table + "." + quote_id (i->name), i->type, *i->member)); @@ -1587,8 +1911,8 @@ namespace relational // insert_one_statement // - os << "const char " << scope << - "::insert_one_statement[] =" << endl; + os << "const char " << scope << "::" << endl + << "insert_one_statement[] =" << endl; if (inverse) os << strlit ("") << ";" @@ -1652,8 +1976,8 @@ namespace relational // delete_all_statement // - os << "const char " << scope << - "::delete_all_statement[] =" << endl; + os << "const char " << scope << "::" << endl + << "delete_all_statement[] =" << endl; if (inverse) os << strlit ("") << ";" @@ -2683,12 +3007,19 @@ namespace relational class_ () : grow_base_ (index_), grow_member_ (index_), + grow_version_member_ (index_, "version_"), + grow_discriminator_member_ (index_, "discriminator_"), bind_id_member_ ("id_"), bind_version_member_ ("version_"), + bind_discriminator_member_ ("discriminator_"), init_id_image_member_ ("id_", "id"), init_version_image_member_ ("version_", "(*v)"), init_id_value_member_ ("id"), - init_version_value_member_ ("v") + init_version_value_member_ ("v"), + init_named_version_value_member_ ("v", "version_"), + init_discriminator_value_member_ ("d", "", false), + init_named_discriminator_value_member_ ( + "d", "discriminator_", false) { init (); } @@ -2698,12 +3029,19 @@ namespace relational context (), grow_base_ (index_), grow_member_ (index_), + grow_version_member_ (index_, "version_"), + grow_discriminator_member_ (index_, "discriminator_"), bind_id_member_ ("id_"), bind_version_member_ ("version_"), + bind_discriminator_member_ ("discriminator_"), init_id_image_member_ ("id_", "id"), init_version_image_member_ ("version_", "(*v)"), init_id_value_member_ ("id"), - init_version_value_member_ ("v") + init_version_value_member_ ("v"), + init_named_version_value_member_ ("v", "version_"), + init_discriminator_value_member_ ("d", "", false), + init_named_discriminator_value_member_ ( + "d", "discriminator_", false) { init (); } @@ -2819,2024 +3157,53 @@ namespace relational } virtual void - traverse_object (type& c) - { - bool abstract (context::abstract (c)); - string const& type (class_fq_name (c)); - string traits ("access::object_traits< " + type + " >"); + traverse_object (type& c); - bool has_ptr (has_a (c, test_pointer)); + // + // view + // - semantics::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. + virtual void + view_extra (type&) + { + } - semantics::data_member* optimistic (context::optimistic (c)); + virtual void + view_query_statement_ctor_args (type&) + { + os << "sts.connection ()," << endl + << "qs.clause ()," << endl + << "qs.parameters_binding ()," << endl + << "imb"; + } - bool grow (false); - bool grow_id (false); + virtual void + traverse_view (type& c); - if (generate_grow) - { - grow = context::grow (c); - grow_id = (id ? context::grow (*id) : false) || - (optimistic ? context::grow (*optimistic) : false); - } + struct expression + { + explicit + expression (std::string const& v): kind (literal), value (v) {} + expression (view_object* vo): kind (pointer), vo (vo) {} - column_count_type const& cc (column_count (c)); + enum kind_type {literal, pointer}; - os << "// " << class_name (c) << endl - << "//" << endl - << endl; + kind_type kind; + std::string value; + data_member_path member_path; + view_object* vo; + }; - object_extra (c); - - // - // Query. - // - - if (options.generate_query ()) - { - // query_columns_base - // - if (has_ptr) - { - instance t (c); - t->traverse (c); - } - } - - // - // Containers (abstract and concrete). - // - bool containers (has_a (c, test_container)); - bool straight_containers (false); - bool straight_readwrite_containers (false); - - if (containers) - { - containers = true; - size_t scn (has_a (c, test_straight_container)); - - if (scn != 0) - { - straight_containers = true; - - // Inverse containers cannot be marked readonly. - // - straight_readwrite_containers = - scn > has_a (c, test_readonly_container); - } - } - - if (containers) - { - instance t (c); - t->traverse (c); - } - - // - // Functions (abstract and concrete). - // - - // id (image_type) - // - if (id != 0 && options.generate_query () && !base_id) - { - os << traits << "::id_type" << endl - << traits << "::" << endl - << "id (const image_type& i)" - << "{" - << db << "::database* db (0);" - << "ODB_POTENTIALLY_UNUSED (db);" - << endl - << "id_type id;"; - init_id_value_member_->traverse (*id); - os << "return id;" - << "}"; - } - - if (id != 0 && optimistic != 0 && !base_id) - { - os << traits << "::version_type" << endl - << traits << "::" << endl - << "version (const image_type& i)" - << "{" - << "version_type v;"; - init_version_value_member_->traverse (*optimistic); - os << "return v;" - << "}"; - } - - // grow () - // - if (generate_grow) - { - os << "bool " << traits << "::" << endl - << "grow (image_type& i, " << truncated_vector << " t)" - << "{" - << "ODB_POTENTIALLY_UNUSED (i);" - << "ODB_POTENTIALLY_UNUSED (t);" - << endl - << "bool grew (false);" - << endl; - - index_ = 0; - inherits (c, grow_base_inherits_); - names (c, grow_member_names_); - - os << "return grew;" - << "}"; - } - - // bind (image_type) - // - os << "void " << traits << "::" << endl - << "bind (" << bind_vector << " b, image_type& i, " << - db << "::statement_kind sk)" - << "{" - << "ODB_POTENTIALLY_UNUSED (sk);" - << endl - << "using namespace " << db << ";" - << endl; - - if (readonly (c)) - os << "assert (sk != statement_update);" - << endl; - - os << "std::size_t n (0);" - << endl; - - inherits (c, bind_base_inherits_); - names (c, bind_member_names_); - - os << "}"; - - // bind (id_image_type) - // - if (id != 0 && !base_id) - { - os << "void " << traits << "::" << endl - << "bind (" << bind_vector << " b, id_image_type& i)" - << "{" - << "std::size_t n (0);"; - - if (composite_wrapper (utype (*id))) - os << db << "::statement_kind sk (" << db << "::statement_select);"; - - bind_id_member_->traverse (*id); - - if (optimistic != 0) - { - os << "n += " << column_count (c).id << ";" - << endl; - - bind_version_member_->traverse (*optimistic); - } - - os << "}"; - } - - // init (image, object) - // - os << "bool " << traits << "::" << endl - << "init (image_type& i, const object_type& o, " << - db << "::statement_kind sk)" - << "{" - << "ODB_POTENTIALLY_UNUSED (i);" - << "ODB_POTENTIALLY_UNUSED (o);" - << "ODB_POTENTIALLY_UNUSED (sk);" - << endl - << "using namespace " << db << ";" - << endl; - - if (readonly (c)) - os << "assert (sk != statement_update);" - << endl; - - init_image_pre (c); - - os << "bool grew (false);" - << endl; - - inherits (c, init_image_base_inherits_); - names (c, init_image_member_names_); - - os << "return grew;" - << "}"; - - // init (object, image) - // - os << "void " << traits << "::" << endl - << "init (object_type& o, const image_type& i, database* db)" - << "{" - << "ODB_POTENTIALLY_UNUSED (o);" - << "ODB_POTENTIALLY_UNUSED (i);" - << "ODB_POTENTIALLY_UNUSED (db);" - << endl; - - inherits (c, init_value_base_inherits_); - names (c, init_value_member_names_); - - os << "}"; - - // init (id_image, id) - // - if (id != 0 && !base_id) - { - os << "void " << traits << "::" << endl - << "init (id_image_type& i, const id_type& id" << - (optimistic != 0 ? ", const version_type* v" : "") << ")" - << "{"; - - if (grow_id) - os << "bool grew (false);"; - - if (composite_wrapper (utype (*id))) - os << db << "::statement_kind sk (" << db << "::statement_select);"; - - init_id_image_member_->traverse (*id); - - if (optimistic != 0) - { - // Here we rely on the fact that init_image_member - // always wraps the statements in a block. - // - os << "if (v != 0)"; - init_version_image_member_->traverse (*optimistic); - } - - if (grow_id) - os << "if (grew)" << endl - << "i.version++;"; - - os << "}"; - } - - // - // The rest only applies to concrete objects. - // - if (abstract) - return; - - // - // Containers (concrete). - // - - // Statement cache (definition). - // - if (id != 0) - { - os << "struct " << traits << "::container_statement_cache_type" - << "{"; - - instance cm; - cm->traverse (c); - - os << (containers ? "\n" : "") - << "container_statement_cache_type (" << db << "::connection&" << - (containers ? " c" : "") << ")"; - - instance im; - im->traverse (c); - - os << "{" - << "}" - << "};"; - } - - // - // Statements. - // - - string const& table (table_qname (c)); - - // persist_statement - // - { - statement_columns sc; - { - statement_kind sk (statement_insert); // Imperfect forwarding. - instance ct (sk, sc); - ct->traverse (c); - process_statement_columns (sc, statement_insert); - } - - bool dv (sc.empty ()); // The DEFAULT VALUES syntax. - - os << "const char " << traits << "::persist_statement[] " << - "=" << endl - << strlit ("INSERT INTO " + table_qname(c) + - (dv ? "" : " (")) << endl; - - for (statement_columns::const_iterator i (sc.begin ()), - e (sc.end ()); i != e;) - { - string const& c (i->column); - os << strlit (c + (++i != e ? "," : ")")) << endl; - } - - instance qp; - - persist_statement_extra (c, *qp, persist_after_columns); - - if (!dv) - { - string values; - instance pt (values, *qp); - pt->traverse (c); - os << strlit (" VALUES (" + values + ")"); - } - else - os << strlit (" DEFAULT VALUES"); - - persist_statement_extra (c, *qp, persist_after_values); - - os << ";" - << endl; - } - - if (id != 0) - { - instance id_cols; - id_cols->traverse (*id); - - // find_statement - // - { - statement_columns sc; - { - statement_kind sk (statement_select); // Imperfect forwarding. - instance t (table, sk, sc); - t->traverse (c); - process_statement_columns (sc, statement_select); - } - - os << "const char " << traits << "::find_statement[] =" << endl - << strlit ("SELECT ") << endl; - - for (statement_columns::const_iterator i (sc.begin ()), - e (sc.end ()); i != e;) - { - string const& c (i->column); - os << strlit (c + (++i != e ? "," : "")) << endl; - } - - os << strlit (" FROM " + table) << endl; - - bool f (false); // @@ (im)perfect forwarding - instance j (c, f); // @@ (im)perfect forwarding - j->traverse (c); - j->write (); - - instance qp; - for (object_columns_list::iterator b (id_cols->begin ()), i (b); - i != id_cols->end (); ++i) - { - if (i != b) - os << endl; - - os << strlit ((i == b ? " WHERE " : " AND ") + table + "." + - quote_id (i->name) + "=" + qp->next ()); - } - - os << ";" - << endl; - } - - // update_statement - // - if (cc.total != cc.id + cc.inverse + cc.readonly) - { - instance qp; - - statement_columns sc; - { - query_parameters* p (qp.get ()); // Imperfect forwarding. - statement_kind sk (statement_update); // Imperfect forwarding. - instance t (sk, sc, p); - t->traverse (c); - process_statement_columns (sc, statement_update); - } - - os << "const char " << traits << "::update_statement[] " << - "=" << endl - << strlit ("UPDATE " + table + " SET ") << endl; - - for (statement_columns::const_iterator i (sc.begin ()), - e (sc.end ()); i != e;) - { - string const& c (i->column); - os << strlit (c + (++i != e ? "," : "")) << endl; - } - - for (object_columns_list::iterator b (id_cols->begin ()), i (b); - i != id_cols->end (); ++i) - { - if (i != b) - os << endl; - - os << strlit ((i == b ? " WHERE " : " AND ") + - quote_id (i->name) + "=" + qp->next ()); - } - - if (optimistic != 0) - os << endl - << strlit (" AND " + column_qname (*optimistic) + - "=" + qp->next ()); - - os << ";" - << endl; - } - - // erase_statement - // - { - instance qp; - os << "const char " << traits << "::erase_statement[] =" << endl - << strlit ("DELETE FROM " + table); - - for (object_columns_list::iterator b (id_cols->begin ()), i (b); - i != id_cols->end (); ++i) - { - - os << endl - << strlit ((i == b ? " WHERE " : " AND ") + - quote_id (i->name) + "=" + qp->next ()); - } - - os << ";" - << endl; - } - - if (optimistic != 0) - { - instance qp; - - os << "const char " << traits << - "::optimistic_erase_statement[] =" << endl - << strlit ("DELETE FROM " + table); - - for (object_columns_list::iterator b (id_cols->begin ()), i (b); - i != id_cols->end (); ++i) - { - os << endl - << strlit ((i == b ? " WHERE " : " AND ") + - quote_id (i->name) + "=" + qp->next ()); - } - - os << endl - << strlit (" AND " + column_qname (*optimistic) + - "=" + qp->next ()) << ";" - << endl; - } - } - - if (options.generate_query ()) - { - // query_statement - // - statement_columns sc; - { - statement_kind sk (statement_select); // Imperfect forwarding. - instance oc (table, sk, sc); - oc->traverse (c); - process_statement_columns (sc, statement_select); - } - - os << "const char " << traits << "::query_statement[] =" << endl - << strlit ("SELECT ") << endl; - - for (statement_columns::const_iterator i (sc.begin ()), - e (sc.end ()); i != e;) - { - string const& c (i->column); - os << strlit (c + (++i != e ? "," : "")) << endl; - } - - os << strlit (" FROM " + table) << endl; - - if (id != 0) - { - bool t (true); //@@ (im)perfect forwarding - instance oj (c, t); //@@ (im)perfect forwarding - oj->traverse (c); - oj->write (); - } - - os << strlit (" ") << ";" - << endl; - - // erase_query_statement - // - os << "const char " << traits << "::erase_query_statement[] =" << endl - << strlit ("DELETE FROM " + table) << endl; - - // DELETE JOIN: - // - // MySQL: - // << strlit ("DELETE FROM " + table + " USING " + table) << endl; - // << strlit ("DELETE " + table + " FROM " + table) << endl; - // oj->write (); - // - - os << strlit (" ") << ";" - << endl; - - // table_name - // - os << "const char " << traits << "::table_name[] =" << endl - << strlit (table_qname (c)) << ";" // Use quoted name. - << endl; - } - - // persist () - // - char const* object_statements_type ( - id != 0 - ? "object_statements< object_type >" - : "object_statements_no_id< object_type >"); - - os << "void " << traits << "::" << endl - << "persist (database&, " << (auto_id ? "" : "const ") << - "object_type& obj)" - << "{" - << "using namespace " << db << ";" - << endl - << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" - << object_statements_type << "& sts (" << endl - << "conn.statement_cache ().find_object ());" - << "image_type& im (sts.image ());" - << "binding& imb (sts.insert_image_binding ());" - << endl - << "if (init (im, obj, statement_insert))" << endl - << "im.version++;" - << endl; - - if (auto_id && insert_send_auto_id) - { - string const& n (id->name ()); - string var ("im." + n + (n[n.size () - 1] == '_' ? "" : "_")); - init_auto_id (*id, var); - os << endl; - } - - os << "if (im.version != sts.insert_image_version () || " << - "imb.version == 0)" - << "{" - << "bind (imb.bind, im, statement_insert);" - << "sts.insert_image_version (im.version);" - << "imb.version++;" - << "}" - << "insert_statement& st (sts.persist_statement ());" - << "if (!st.execute ())" << endl - << "throw object_already_persistent ();" - << endl; - - if (auto_id) - { - if (const_type (id->type ())) - os << "const_cast< id_type& > (obj." << id->name () << ")"; - else - os << "obj." << id->name (); - - os << " = static_cast< id_type > (st.id ());" - << endl; - } - - if (optimistic != 0) - { - // Set the version in the object member. - // - if (!auto_id || const_type (optimistic->type ())) - os << "const_cast< version_type& > (" << - "obj." << optimistic->name () << ") = 1;"; - else - os << "obj." << optimistic->name () << " = 1;"; - - os << endl; - } - - if (straight_containers) - { - // Initialize id_image and binding. - // - os << "id_image_type& i (sts.id_image ());" - << "init (i, obj." << id->name () << ");" - << endl - << "binding& idb (sts.id_image_binding ());" - << "if (i.version != sts.id_image_version () || idb.version == 0)" - << "{" - << "bind (idb.bind, i);" - << "sts.id_image_version (i.version);" - << "idb.version++;" - << "}"; - - instance t (container_calls::persist_call); - t->traverse (c); - } - - os << "}"; - - // update () - // - if (id != 0 && !readonly (c)) - { - os << "void " << traits << "::" << endl - << "update (database&, const object_type& obj)" - << "{" - << "using namespace " << db << ";" - << endl - << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" - << object_statements_type << "& sts (" << endl - << "conn.statement_cache ().find_object ());" - << endl; - - if (cc.total != cc.id + cc.inverse + cc.readonly) - { - // Initialize object and id images. - // - os << "id_image_type& i (sts.id_image ());"; - - if (optimistic == 0) - os << "init (i, obj." << id->name () << ");"; - else - os << "init (i, obj." << id->name () << ", &obj." << - optimistic->name () << ");"; - - os << endl - << "image_type& im (sts.image ());" - << "if (init (im, obj, statement_update))" << endl - << "im.version++;" - << endl; - - // Update binding is bound to two images (object and id) - // so we have to track both versions. - // - os << "bool u (false);" // Avoid incrementing version twice. - << "binding& imb (sts.update_image_binding ());" - << "if (im.version != sts.update_image_version () || " << - "imb.version == 0)" - << "{" - << "bind (imb.bind, im, statement_update);" - << "sts.update_image_version (im.version);" - << "imb.version++;" - << "u = true;" - << "}"; - - // To update the id part of the update binding we have to do - // it indirectly via the id binding, which just points to the - // suffix of the update bind array (see object_statements). - // - os << "binding& idb (sts.id_image_binding ());" - << "if (i.version != sts.update_id_image_version () || " << - "idb.version == 0)" - << "{" - // If the id binding is up-to-date, then that means update - // binding is too and we just need to update the versions. - // - << "if (i.version != sts.id_image_version () || " << - "idb.version == 0)" - << "{" - << "bind (idb.bind, i);" - // Update the id binding versions since we may use them later - // to update containers. - // - << "sts.id_image_version (i.version);" - << "idb.version++;" - << "}" - << "sts.update_id_image_version (i.version);" - << endl - << "if (!u)" << endl - << "imb.version++;" - << "}"; - - os << "if (sts.update_statement ().execute () == 0)" << endl; - - if (optimistic == 0) - os << "throw object_not_persistent ();"; - else - os << "throw object_changed ();"; - - os << endl; - } - else - { - // We don't have any columns to update. Note that we still have - // to make sure this object exists in the database. For that we - // will run the SELECT query using the find_() function. - // - os << "if (!find_ (sts, obj." << id->name () << "))" << endl - << "throw object_not_persistent ();" - << endl; - - if (delay_freeing_statement_result) - os << "sts.find_statement ().free_result ();"; - - if (straight_readwrite_containers) - os << "binding& idb (sts.id_image_binding ());" - << endl; - } - - if (straight_readwrite_containers) - { - instance t (container_calls::update_call); - t->traverse (c); - } - - if (optimistic != 0) - { - // Update version in the object member. - // - os << "const_cast< version_type& > (" << - "obj." << optimistic->name () << ")++;"; - } - - os << "}"; - } - - // erase (id_type) - // - if (id != 0) - { - os << "void " << traits << "::" << endl - << "erase (database&, const id_type& id)" - << "{" - << "using namespace " << db << ";" - << endl - << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" - << object_statements_type << "& sts (" << endl - << "conn.statement_cache ().find_object ());" - << endl; - - // Initialize id image. - // - os << "id_image_type& i (sts.id_image ());" - << "init (i, id);" - << endl; - - os << "binding& idb (sts.id_image_binding ());" - << "if (i.version != sts.id_image_version () || idb.version == 0)" - << "{" - << "bind (idb.bind, i);" - << "sts.id_image_version (i.version);" - << "idb.version++;" - << "}"; - - // Erase containers first so that there are no reference - // violations (we don't want to reply on ON DELETE CASCADE - // here since in case of a custom schema, it might not be - // there). - // - if (straight_containers) - { - instance t (container_calls::erase_call); - t->traverse (c); - } - - os << "if (sts.erase_statement ().execute () != 1)" << endl - << "throw object_not_persistent ();"; - - os << "}"; - } - - // erase (object_type) - // - if (id != 0 && optimistic != 0) - { - os << "void " << traits << "::" << endl - << "erase (database&, const object_type& obj)" - << "{" - << "using namespace " << db << ";" - << endl - << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" - << object_statements_type << "& sts (" << endl - << "conn.statement_cache ().find_object ());" - << endl; - - // Initialize id + managed column image. - // - os << "id_image_type& i (sts.id_image ());" - << "init (i, obj." << id->name () << ", &obj." << - optimistic->name () << ");" - << endl; - - // To update the id part of the optimistic id binding we have - // to do it indirectly via the id binding, since both id and - // optimistic id bindings just point to the suffix of the - // update bind array (see object_statements). - // - os << "binding& idb (sts.id_image_binding ());" - << "binding& oidb (sts.optimistic_id_image_binding ());" - << "if (i.version != sts.optimistic_id_image_version () || " << - "oidb.version == 0)" - << "{" - // If the id binding is up-to-date, then that means optimistic - // id binding is too and we just need to update the versions. - // - << "if (i.version != sts.id_image_version () || idb.version == 0)" - << "{" - << "bind (idb.bind, i);" - // Update the id binding versions since we may use them later - // to delete containers. - // - << "sts.id_image_version (i.version);" - << "idb.version++;" - << "}" - << "sts.optimistic_id_image_version (i.version);" - << "oidb.version++;" - << "}"; - - // Erase containers first so that there are no reference - // violations (we don't want to rely on ON DELETE CASCADE - // here since in case of a custom schema, it might not be - // there). - // - if (straight_containers) - { - // Things get complicated here: we don't want to trash the - // containers and then find out that the versions don't match - // and we therefore cannot delete the object. After all, there - // is no guarantee that the user will abort the transaction. - // In fact, a perfectly reasonable scenario is to reload the - // object, re-apply the changes, and commit the transaction. - // - // There doesn't seem to be anything better than first making - // sure we can delete the object, then deleting the container - // data, and then deleting the object. To check that we can - // delete the object we are going to use find_() and then - // compare the versions. A special-purpose SELECT query would - // have been more efficient but it would complicated and bloat - // things significantly. - // - - os << "if (!find_ (sts, obj." << id->name () << "))" << endl - << "throw object_changed ();" - << endl; - - if (delay_freeing_statement_result) - os << "sts.find_statement ().free_result ();"; - - os << "if (version (sts.image ()) != obj." << - optimistic->name () << ")" << endl - << "throw object_changed ();" - << endl; - - instance t (container_calls::erase_call); - t->traverse (c); - } - - os << "if (sts.optimistic_erase_statement ().execute () != 1)" << endl - << "throw object_changed ();" - << "}"; - } - - // find (id) - // - if (id != 0 && c.default_ctor ()) - { - os << traits << "::pointer_type" << endl - << traits << "::" << endl - << "find (database& db, const id_type& id)" - << "{" - << "using namespace " << db << ";" - << endl - << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" - << object_statements_type << "& sts (" << endl - << "conn.statement_cache ().find_object ());" - << object_statements_type << "::auto_lock l (sts);"; - - if (delay_freeing_statement_result) - os << "auto_result ar;"; - - os << endl - << "if (l.locked ())" - << "{" - << "if (!find_ (sts, id))" << endl - << "return pointer_type ();"; - - if (delay_freeing_statement_result) - os << endl - << "ar.set (sts.find_statement ());"; - - os << "}" - << "pointer_type p (" << endl - << "access::object_factory< object_type, pointer_type >::create ());" - << "pointer_traits< pointer_type >::guard pg (p);" - << "pointer_cache_traits< pointer_type >::insert_guard ig (" << endl - << "pointer_cache_traits< pointer_type >::insert (db, id, p));" - << "object_type& obj (pointer_traits< pointer_type >::get_ref (p));" - << endl - << "if (l.locked ())" - << "{" - << "callback (db, obj, callback_event::pre_load);" - << "init (obj, sts.image (), &db);"; - - init_value_extra (); - - if (delay_freeing_statement_result) - os << "ar.free ();"; - - os << "load_ (sts, obj);" - << "sts.load_delayed ();" - << "l.unlock ();" - << "callback (db, obj, callback_event::post_load);" - << "}" - << "else" << endl - << "sts.delay_load (id, obj, ig.position ());" - << endl; - - os << "ig.release ();" - << "pg.release ();" - << "return p;" - << "}"; - } - - // find (id, obj) - // - if (id != 0) - { - os << "bool " << traits << "::" << endl - << "find (database& db, const id_type& id, object_type& obj)" - << "{" - << "using namespace " << db << ";" - << endl - << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" - << object_statements_type << "& sts (" << endl - << "conn.statement_cache ().find_object ());" - << endl - // This can only be top-level call so auto_lock must succeed. - // - << object_statements_type << "::auto_lock l (sts);" - << endl; - - os << "if (!find_ (sts, id))" << endl - << "return false;" - << endl; - - if (delay_freeing_statement_result) - os << "auto_result ar (sts.find_statement ());"; - - os << "reference_cache_traits< object_type >::insert_guard ig (" << endl - << "reference_cache_traits< object_type >::insert (db, id, obj));" - << endl - << "callback (db, obj, callback_event::pre_load);" - << "init (obj, sts.image (), &db);"; - - init_value_extra (); - - if (delay_freeing_statement_result) - os << "ar.free ();"; - - os << "load_ (sts, obj);" - << "sts.load_delayed ();" - << "l.unlock ();" - << "callback (db, obj, callback_event::post_load);" - << "ig.release ();" - << "return true;" - << "}"; - } - - // reload() - // - if (id != 0) - { - os << "bool " << traits << "::" << endl - << "reload (database& db, object_type& obj)" - << "{" - << "using namespace " << db << ";" - << endl - << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" - << object_statements_type << "& sts (" << endl - << "conn.statement_cache ().find_object ());" - << endl - // This can only be top-level call so auto_lock must succeed. - // - << object_statements_type << "::auto_lock l (sts);" - << endl; - - os << "if (!find_ (sts, obj." << id->name () << "))" << endl - << "return false;" - << endl; - - if (delay_freeing_statement_result) - os << "auto_result ar (sts.find_statement ());" - << endl; - - if (optimistic != 0) - { - os << "if (version (sts.image ()) == obj." << - optimistic->name () << ")" << endl - << "return true;"; - } - - os << "callback (db, obj, callback_event::pre_load);" - << "init (obj, sts.image (), &db);"; - - init_value_extra (); - - if (delay_freeing_statement_result) - os << "ar.free ();"; - - os << "load_ (sts, obj);" - << "sts.load_delayed ();" - << "l.unlock ();" - << "callback (db, obj, callback_event::post_load);" - << "return true;" - << "}"; - } - - // find_ () - // - if (id != 0) - { - os << "bool " << traits << "::" << endl - << "find_ (" << db << "::" << object_statements_type << "& " << - "sts, const id_type& id)" - << "{" - << "using namespace " << db << ";" - << endl; - - // Initialize id image. - // - os << "id_image_type& i (sts.id_image ());" - << "init (i, id);" - << endl; - - os << "binding& idb (sts.id_image_binding ());" - << "if (i.version != sts.id_image_version () || idb.version == 0)" - << "{" - << "bind (idb.bind, i);" - << "sts.id_image_version (i.version);" - << "idb.version++;" - << "}"; - - // Rebind data image. - // - os << "image_type& im (sts.image ());" - << "binding& imb (sts.select_image_binding ());" - << endl - << "if (im.version != sts.select_image_version () || " << - "imb.version == 0)" - << "{" - << "bind (imb.bind, im, statement_select);" - << "sts.select_image_version (im.version);" - << "imb.version++;" - << "}" - << "select_statement& st (sts.find_statement ());" - << "st.execute ();" - << "auto_result ar (st);" - << "select_statement::result r (st.fetch ());" - << endl; - - if (grow) - os << "if (r == select_statement::truncated)" - << "{" - << "if (grow (im, sts.select_image_truncated ()))" << endl - << "im.version++;" - << endl - << "if (im.version != sts.select_image_version ())" - << "{" - << "bind (imb.bind, im, statement_select);" - << "sts.select_image_version (im.version);" - << "imb.version++;" - << "st.refetch ();" - << "}" - << "}"; - - // If we are delaying, only free the result if it is empty. - // - if (delay_freeing_statement_result) - os << "if (r != select_statement::no_data)" - << "{" - << "ar.release ();" - << "return true;" - << "}" - << "else" << endl - << "return false;"; - else - os << "return r != select_statement::no_data;"; - - os << "}"; - } - - // load_() - // - if (containers) - { - os << "void " << traits << "::" << endl - << "load_ (" << db << "::" << object_statements_type << "& " << - "sts, object_type& obj)" - << "{" - << db << "::binding& idb (sts.id_image_binding ());" - << endl; - instance t (container_calls::load_call); - t->traverse (c); - os << "}"; - } - - if (options.generate_query ()) - { - // query () - // - os << "result< " << traits << "::object_type >" << endl - << traits << "::" << endl - << "query (database&, const query_base_type& q)" - << "{" - << "using namespace " << db << ";" - << "using odb::details::shared;" - << "using odb::details::shared_ptr;" - << endl - << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" - << endl - << object_statements_type << "& sts (" << endl - << "conn.statement_cache ().find_object ());" - << endl - << "image_type& im (sts.image ());" - << "binding& imb (sts.select_image_binding ());" - << endl - << "if (im.version != sts.select_image_version () || " << - "imb.version == 0)" - << "{" - << "bind (imb.bind, im, statement_select);" - << "sts.select_image_version (im.version);" - << "imb.version++;" - << "}" - << "shared_ptr st (" << endl - << "new (shared) select_statement (" << endl; - - object_query_statement_ctor_args (c); - - os << "));" << endl - << "st->execute ();"; - - post_query_ (c); - - char const* result_type ( - id != 0 - ? "object_result_impl" - : "object_result_impl_no_id"); - - os << endl - << "shared_ptr< odb::" << result_type << " > r (" << endl - << "new (shared) " << db << "::" << result_type << " (" << endl - << "q, st, sts));" - << endl - << "return result (r);" - << "}"; - - // erase_query - // - os << "unsigned long long " << traits << "::" << endl - << "erase_query (database&, const query_base_type& q)" - << "{" - << "using namespace " << db << ";" - << endl - << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" - << endl - << "delete_statement st (" << endl; - - object_erase_query_statement_ctor_args (c); - - os << ");" - << endl - << "return st.execute ();" - << "}"; - } - - if (embedded_schema) - schema_->traverse (c); - } - - // - // view - // - - virtual void - view_extra (type&) - { - } - - virtual void - view_query_statement_ctor_args (type&) - { - os << "sts.connection ()," << endl - << "qs.clause ()," << endl - << "qs.parameters_binding ()," << endl - << "imb"; - } - - virtual void - traverse_view (type& c) - { - string const& type (class_fq_name (c)); - string traits ("access::view_traits< " + type + " >"); - - os << "// " << class_name (c) << endl - << "//" << endl - << endl; - - view_extra (c); - - // - // Query. - // - - // query_type - // - size_t obj_count (c.get ("object-count")); - - if (obj_count != 0) - { - view_objects& objs (c.get ("objects")); - - if (obj_count > 1) - { - for (view_objects::const_iterator i (objs.begin ()); - i < objs.end (); - ++i) - { - if (i->kind != view_object::object) - continue; // Skip tables. - - qname const& t (table_name (*i->obj)); - - if (!i->alias.empty () && - (t.qualified () || i->alias != t.uname ())) - os << "const char " << traits << "::query_columns::" << endl - << i->alias << "_alias_[] = " << - strlit (quote_id (i->alias)) << ";" - << endl; - } - } - else - { - // For a single object view we generate a shortcut without - // an intermediate typedef. - // - view_object const* vo (0); - for (view_objects::const_iterator i (objs.begin ()); - vo == 0 && i < objs.end (); - ++i) - { - if (i->kind == view_object::object) - vo = &*i; - } - - qname const& t (table_name (*vo->obj)); - - if (!vo->alias.empty () && - (t.qualified () || vo->alias != t.uname ())) - os << "const char " << traits << "::" << endl - << "query_alias[] = " << strlit (quote_id (vo->alias)) << ";" - << endl; - } - } - - // - // Functions. - // - - // grow () - // - if (generate_grow) - { - os << "bool " << traits << "::" << endl - << "grow (image_type& i, " << truncated_vector << " t)" - << "{" - << "ODB_POTENTIALLY_UNUSED (i);" - << "ODB_POTENTIALLY_UNUSED (t);" - << endl - << "bool grew (false);" - << endl; - - index_ = 0; - names (c, grow_member_names_); - - os << "return grew;" - << "}"; - } - - // bind (image_type) - // - os << "void " << traits << "::" << endl - << "bind (" << bind_vector << " b, image_type& i)" - << "{" - << "using namespace " << db << ";" - << endl - << db << "::statement_kind sk (statement_select);" - << "ODB_POTENTIALLY_UNUSED (sk);" - << endl - << "std::size_t n (0);" - << endl; - - names (c, bind_member_names_); - - os << "}"; - - // init (view, image) - // - os << "void " << traits << "::" << endl - << "init (view_type& o, const image_type& i, database* db)" - << "{" - << "ODB_POTENTIALLY_UNUSED (o);" - << "ODB_POTENTIALLY_UNUSED (i);" - << "ODB_POTENTIALLY_UNUSED (db);" - << endl; - - names (c, init_value_member_names_); - - os << "}"; - - // query_statement() - // - view_query& vq (c.get ("query")); - - if (vq.kind != view_query::runtime) - { - os << traits << "::query_base_type" << endl - << traits << "::" << endl - << "query_statement (const query_base_type& q)" - << "{"; - - if (vq.kind == view_query::complete) - { - os << "query_base_type r (" << endl; - - bool ph (false); - - if (!vq.literal.empty ()) - { - // See if we have the '(?)' placeholder. - // - // @@ Ideally we would need to make sure we don't match - // this inside strings and quoted identifier. So the - // proper way to handle this would be to tokenize the - // statement using sql_lexer, once it is complete enough. - // - string::size_type p (vq.literal.find ("(?)")); - - if (p != string::npos) - { - ph = true; - os << strlit (string (vq.literal, 0, p + 1)) << " +" << endl - << "(q.empty () ? query_base_type::true_expr : q) +" << endl - << strlit (string (vq.literal, p + 2)); - } - else - os << strlit (vq.literal); - } - else - // Output the pragma location for easier error tracking. - // - os << "// From " << - location_file (vq.loc).leaf () << ":" << - location_line (vq.loc) << ":" << - location_column (vq.loc) << endl - << translate_expression ( - c, vq.expr, vq.scope, vq.loc, "query", &ph).value; - - os << ");"; - - // If there was no placeholder, add the query condition - // at the end. - // - if (!ph) - os << "r += q.clause_prefix ();" - << "r += q;"; - } - else // vq.kind == view_query::condition - { - statement_columns sc; - { - instance t (sc); - t->traverse (c); - process_statement_columns (sc, statement_select); - } - - os << "query_base_type r (" << endl - << strlit ("SELECT ") << endl; - - for (statement_columns::const_iterator i (sc.begin ()), - e (sc.end ()); i != e;) - { - string const& c (i->column); - os << strlit (c + (++i != e ? "," : "")) << endl; - } - - os << ");" - << endl; - - // Generate from-list. - // - view_objects const& objs (c.get ("objects")); - - for (view_objects::const_iterator i (objs.begin ()); - i != objs.end (); - ++i) - { - bool first (i == objs.begin ()); - string l; - - // - // Tables. - // - - if (i->kind == view_object::table) - { - if (first) - { - l = "FROM "; - l += quote_id (i->tbl_name); - - if (!i->alias.empty ()) - l += (need_alias_as ? " AS " : " ") + quote_id (i->alias); - - os << "r += " << strlit (l) << ";" - << endl; - - continue; - } - - l = "LEFT JOIN "; - l += quote_id (i->tbl_name); - - if (!i->alias.empty ()) - l += (need_alias_as ? " AS " : " ") + quote_id (i->alias); - - expression e ( - translate_expression ( - c, i->cond, i->scope, i->loc, "table")); - - if (e.kind != expression::literal) - { - error (i->loc) - << "invalid join condition in db pragma table" << endl; - - throw operation_failed (); - } - - l += " ON"; - - os << "r += " << strlit (l) << ";" - // Output the pragma location for easier error tracking. - // - << "// From " << - location_file (i->loc).leaf () << ":" << - location_line (i->loc) << ":" << - location_column (i->loc) << endl - << "r += " << e.value << ";" - << endl; - - continue; - } - - // - // Objects. - // - - // First object. - // - if (first) - { - l = "FROM "; - l += table_qname (*i->obj); - - if (!i->alias.empty ()) - l += (need_alias_as ? " AS " : " ") + quote_id (i->alias); - - os << "r += " << strlit (l) << ";" - << endl; - - continue; - } - - expression e ( - translate_expression ( - c, i->cond, i->scope, i->loc, "object")); - - // Literal expression. - // - if (e.kind == expression::literal) - { - l = "LEFT JOIN "; - l += table_qname (*i->obj); - - if (!i->alias.empty ()) - l += (need_alias_as ? " AS " : " ") + quote_id (i->alias); - - l += " ON"; - - os << "r += " << strlit (l) << ";" - // Output the pragma location for easier error tracking. - // - << "// From " << - location_file (i->loc).leaf () << ":" << - location_line (i->loc) << ":" << - location_column (i->loc) << endl - << "r += " << e.value << ";" - << endl; - - continue; - } - - // We have an object relationship (pointer) for which we need - // to come up with the corresponding JOIN condition. If this - // is a to-many relationship, then we first need to JOIN the - // container table. This code is similar to object_joins. - // - using semantics::data_member; - - data_member& m (*e.member_path.back ()); - - // Resolve the pointed-to object to view_object and do - // some sanity checks while at it. - // - semantics::class_* c (0); - - if (semantics::type* cont = container (m)) - c = object_pointer (container_vt (*cont)); - else - c = object_pointer (utype (m)); - - view_object const* vo (0); - - // Check if the pointed-to object has been previously - // associated with this view and is unambiguous. A - // pointer to ourselves is always assumed to point - // to this association. - // - if (i->obj == c) - vo = &*i; - else - { - bool ambig (false); - - for (view_objects::const_iterator j (objs.begin ()); - j != i; - ++j) - { - if (j->obj != c) - continue; - - if (vo == 0) - { - vo = &*j; - continue; - } - - // If it is the first ambiguous object, issue the - // error. - // - if (!ambig) - { - error (i->loc) - << "pointed-to object '" << class_name (*c) << "' is " - << "ambiguous" << endl; - - info (i->loc) - << "candidates are:" << endl; - - info (vo->loc) - << " '" << vo->name () << "'" << endl; - - ambig = true; - } - - info (j->loc) - << " '" << j->name () << "'" << endl; - } - - if (ambig) - { - info (i->loc) - << "use the full join condition clause in db pragma " - << "object to resolve this ambiguity" << endl; - - throw operation_failed (); - } - - if (vo == 0) - { - error (i->loc) - << "pointed-to object '" << class_name (*c) << "' " - << "specified in the join condition has not been " - << "previously associated with this view" << endl; - - throw operation_failed (); - } - } - - // Left and right-hand side table names. - // - qname lt (e.vo->alias.empty () - ? table_name (*e.vo->obj) - : qname (e.vo->alias)); - - qname rt (vo->alias.empty () - ? table_name (*vo->obj) - : qname (vo->alias)); - - // First join the container table if necessary. - // - data_member* im (inverse (m)); - - semantics::type* cont (container (im != 0 ? *im : m)); - - // Container table. - // - string ct; - if (cont != 0) - { - if (im != 0) - { - // For now a direct member can only be directly in - // the object scope. If this changes, the inverse() - // function would have to return a member path instead - // of just a single member. - // - table_prefix tp ( - context::schema (vo->obj->scope ()), - context::table_name_prefix (vo->obj->scope ()), - table_name (*vo->obj) + "_"); - ct = table_qname (*im, tp); - } - else - ct = table_qname (*e.vo->obj, e.member_path); - } - - if (cont != 0) - { - l = "LEFT JOIN "; - l += ct; - l += " ON"; - os << "r += " << strlit (l) << ";"; - - // If we are the pointed-to object, then we have to turn - // things around. This is necessary to have the proper - // JOIN order. There seems to be a pattern there but it - // is not yet intuitively clear what it means. - // - instance c_cols; // Container columns. - instance o_cols; // Object columns. - - qname* ot; // Object table (either lt or rt). - - if (im != 0) - { - if (i->obj == c) - { - // container.value = pointer.id - // - semantics::data_member& id (*id_member (*e.vo->obj)); - - c_cols->traverse (*im, utype (id), "value", "value"); - o_cols->traverse (id); - ot = < - } - else - { - // container.id = pointed-to.id - // - semantics::data_member& id (*id_member (*vo->obj)); - - c_cols->traverse ( - *im, utype (id), "id", "object_id", vo->obj); - o_cols->traverse (id); - ot = &rt; - } - } - else - { - if (i->obj == c) - { - // container.id = pointer.id - // - semantics::data_member& id (*id_member (*e.vo->obj)); - - c_cols->traverse ( - m, utype (id), "id", "object_id", e.vo->obj); - o_cols->traverse (id); - ot = < - } - else - { - // container.value = pointed-to.id - // - semantics::data_member& id (*id_member (*vo->obj)); - - c_cols->traverse (m, utype (id), "value", "value"); - o_cols->traverse (id); - ot = &rt; - } - } - - for (object_columns_list::iterator b (c_cols->begin ()), i (b), - j (o_cols->begin ()); i != c_cols->end (); ++i, ++j) - { - l.clear (); - - if (i != b) - l += "AND "; - - l += ct; - l += '.'; - l += quote_id (i->name); - l += '='; - l += quote_id (*ot); - l += '.'; - l += quote_id (j->name); - - os << "r += " << strlit (l) << ";"; - } - } - - l = "LEFT JOIN "; - l += table_qname (*i->obj); - - if (!i->alias.empty ()) - l += (need_alias_as ? " AS " : " ") + quote_id (i->alias); - - l += " ON"; - os << "r += " << strlit (l) << ";"; - - if (cont != 0) - { - instance c_cols; // Container columns. - instance o_cols; // Object columns. - - qname* ot; // Object table (either lt or rt). - - if (im != 0) - { - if (i->obj == c) - { - // container.id = pointed-to.id - // - semantics::data_member& id (*id_member (*vo->obj)); - - c_cols->traverse ( - *im, utype (id), "id", "object_id", vo->obj); - o_cols->traverse (id); - ot = &rt; - } - else - { - // container.value = pointer.id - // - semantics::data_member& id (*id_member (*e.vo->obj)); - - c_cols->traverse (*im, utype (id), "value", "value"); - o_cols->traverse (id); - ot = < - } - } - else - { - if (i->obj == c) - { - // container.value = pointed-to.id - // - semantics::data_member& id (*id_member (*vo->obj)); - - c_cols->traverse (m, utype (id), "value", "value"); - o_cols->traverse (id); - ot = &rt; - } - else - { - // container.id = pointer.id - // - semantics::data_member& id (*id_member (*e.vo->obj)); - - c_cols->traverse ( - m, utype (id), "id", "object_id", e.vo->obj); - o_cols->traverse (id); - ot = < - } - } - - for (object_columns_list::iterator b (c_cols->begin ()), i (b), - j (o_cols->begin ()); i != c_cols->end (); ++i, ++j) - { - l.clear (); - - if (i != b) - l += "AND "; - - l += ct; - l += '.'; - l += quote_id (i->name); - l += '='; - l += quote_id (*ot); - l += '.'; - l += quote_id (j->name); - - os << "r += " << strlit (l) << ";"; - } - } - else - { - string col_prefix; - - if (im == 0) - col_prefix = - object_columns_base::column_prefix (e.member_path); - - instance l_cols (col_prefix); - instance r_cols; - - if (im != 0) - { - // our.id = pointed-to.pointer - // - l_cols->traverse (*id_member (*e.vo->obj)); - r_cols->traverse (*im); - } - else - { - // our.pointer = pointed-to.id - // - l_cols->traverse (*e.member_path.back ()); - r_cols->traverse (*id_member (*vo->obj)); - } - - for (object_columns_list::iterator b (l_cols->begin ()), i (b), - j (r_cols->begin ()); i != l_cols->end (); ++i, ++j) - { - l.clear (); - - if (i != b) - l += "AND "; - - l += quote_id (lt); - l += '.'; - l += quote_id (i->name); - l += '='; - l += quote_id (rt); - l += '.'; - l += quote_id (j->name); - - os << "r += " << strlit (l) << ";"; - } - } - - os << endl; - } - - // Generate the query condition. - // - if (!vq.literal.empty () || !vq.expr.empty ()) - { - os << "query_base_type c (" << endl; - - bool ph (false); - - if (!vq.literal.empty ()) - { - // See if we have the '(?)' placeholder. - // - // @@ Ideally we would need to make sure we don't match - // this inside strings and quoted identifier. So the - // proper way to handle this would be to tokenize the - // statement using sql_lexer, once it is complete enough. - // - string::size_type p (vq.literal.find ("(?)")); - - if (p != string::npos) - { - ph = true; - os << strlit (string (vq.literal, 0, p + 1))<< " +" << endl - << "(q.empty () ? query_base_type::true_expr : q) +" << endl - << strlit (string (vq.literal, p + 2)); - } - else - os << strlit (vq.literal); - - os << ");"; - } - else - { - // Output the pragma location for easier error tracking. - // - os << "// From " << - location_file (vq.loc).leaf () << ":" << - location_line (vq.loc) << ":" << - location_column (vq.loc) << endl - << translate_expression ( - c, vq.expr, vq.scope, vq.loc, "query", &ph).value; - - os << ");"; - - // Optimize the query if it had a placeholder. This gets - // rid of useless clauses like WHERE TRUE. - // - if (ph) - os << "c.optimize ();"; - } - - if (!ph) - os << "c += q;"; - - os << "r += c.clause_prefix ();" - << "r += c;" - << endl; - } - else - { - os << "r += q.clause_prefix ();" - << "r += q;" - << endl; - } - } - - os << "return r;" - << "}"; - } - - // query () - // - os << "result< " << traits << "::view_type >" << endl - << traits << "::" << endl - << "query (database&, const query_base_type& q)" - << "{" - << "using namespace " << db << ";" - << "using odb::details::shared;" - << "using odb::details::shared_ptr;" - << endl - << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" - << endl - << "view_statements< view_type >& sts (" << endl - << "conn.statement_cache ().find_view ());" - << endl - << "image_type& im (sts.image ());" - << "binding& imb (sts.image_binding ());" - << endl - << "if (im.version != sts.image_version () || imb.version == 0)" - << "{" - << "bind (imb.bind, im);" - << "sts.image_version (im.version);" - << "imb.version++;" - << "}"; - - if (vq.kind == view_query::runtime) - os << "const query_base_type& qs (q);"; - else - os << "const query_base_type& qs (query_statement (q));"; - - os << "shared_ptr st (" << endl - << "new (shared) select_statement (" << endl; - - view_query_statement_ctor_args (c); - - os << "));" << endl - << "st->execute ();"; - - post_query_ (c); - - os << endl - << "shared_ptr< odb::view_result_impl > r (" << endl - << "new (shared) " << db << "::view_result_impl (" << endl - << "qs, st, sts));" - << endl - << "return result (r);" - << "}"; - } - - struct expression - { - explicit - expression (std::string const& v): kind (literal), value (v) {} - expression (view_object* vo): kind (pointer), vo (vo) {} - - enum kind_type {literal, pointer}; - - kind_type kind; - std::string value; - data_member_path member_path; - view_object* vo; - }; - - expression - translate_expression (type& c, - cxx_tokens const&, - tree scope, - location_t loc, - string const& prag, - bool* placeholder = 0); - // - // composite - // + expression + translate_expression (type& c, + cxx_tokens const&, + tree scope, + location_t loc, + string const& prag, + bool* placeholder = 0); + // + // composite + // virtual void traverse_composite (type& c) @@ -4950,6 +3317,9 @@ namespace relational traversal::inherits grow_base_inherits_; instance grow_member_; traversal::names grow_member_names_; + instance grow_version_member_; + instance grow_discriminator_member_; + instance bind_base_; traversal::inherits bind_base_inherits_; @@ -4957,6 +3327,7 @@ namespace relational traversal::names bind_member_names_; instance bind_id_member_; instance bind_version_member_; + instance bind_discriminator_member_; instance init_image_base_; traversal::inherits init_image_base_inherits_; @@ -4973,6 +3344,9 @@ namespace relational instance init_id_value_member_; instance init_version_value_member_; + instance init_named_version_value_member_; + instance init_discriminator_value_member_; + instance init_named_discriminator_value_member_; instance schema_; }; @@ -4987,11 +3361,18 @@ namespace relational extra_pre (); os << "#include " << endl - << "#include // std::memcpy" << endl - << endl; + << "#include // std::memcpy" << endl; + + if (features.polymorphic_object) + os << "#include " << endl; + + os << endl; os << "#include " << endl; + if (features.polymorphic_object) + os << "#include " << endl; + if (embedded_schema) os << "#include " << endl; @@ -5000,23 +3381,42 @@ namespace relational os << endl; - os << "#include " << endl - << "#include " << endl + os << "#include " << endl << "#include " << endl << "#include " << endl << "#include " << endl << "#include " << endl - << "#include " << endl - << "#include " << endl; + << "#include " << endl; - if (options.generate_query ()) + if (features.simple_object) + os << "#include " << endl; + + if (features.polymorphic_object) + os << "#include " << endl; + + if (features.no_id_object) + os << "#include " << endl; + + if (features.view) os << "#include " << endl; os << "#include " << endl << "#include " << endl; if (options.generate_query ()) - os << "#include " << endl; + { + if (features.simple_object) + os << "#include " << endl; + + if (features.polymorphic_object) + os << "#include " << endl; + + if (features.no_id_object) + os << "#include " << endl; + + if (features.view) + os << "#include " << endl; + } extra_post (); diff --git a/odb/relational/sqlite/context.cxx b/odb/relational/sqlite/context.cxx index 9f25fcc..3b8efb6 100644 --- a/odb/relational/sqlite/context.cxx +++ b/odb/relational/sqlite/context.cxx @@ -67,8 +67,9 @@ namespace relational context (ostream& os, semantics::unit& u, options_type const& ops, + features_type& f, sema_rel::model* m) - : root_context (os, u, ops, data_ptr (new (shared) data (os))), + : root_context (os, u, ops, f, data_ptr (new (shared) data (os))), base_context (static_cast (root_context::data_.get ()), m), data_ (static_cast (base_context::data_)) { @@ -79,6 +80,7 @@ namespace relational need_alias_as = true; insert_send_auto_id = true; delay_freeing_statement_result = false; + need_image_clone = false; data_->bind_vector_ = "sqlite::bind*"; data_->truncated_vector_ = "bool*"; diff --git a/odb/relational/sqlite/context.hxx b/odb/relational/sqlite/context.hxx index 4123b0c..3c17d55 100644 --- a/odb/relational/sqlite/context.hxx +++ b/odb/relational/sqlite/context.hxx @@ -73,6 +73,7 @@ namespace relational context (std::ostream&, semantics::unit&, options_type const&, + features_type& f, sema_rel::model*); static context& diff --git a/odb/relational/sqlite/source.cxx b/odb/relational/sqlite/source.cxx index c170904..1e93568 100644 --- a/odb/relational/sqlite/source.cxx +++ b/odb/relational/sqlite/source.cxx @@ -21,127 +21,17 @@ namespace relational // bind // - struct bind_member: relational::bind_member, member_base + struct bind_member: relational::bind_member_impl, + member_base { bind_member (base const& x) - : member_base::base (x), // virtual base - base (x), + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), member_base (x) { } - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - ostringstream ostr; - ostr << "b[n]"; - b = ostr.str (); - - arg = arg_override_.empty () ? string ("i") : arg_override_; - - if (var_override_.empty ()) - { - os << "// " << mi.m.name () << endl - << "//" << endl; - - if (inverse (mi.m, key_prefix_) || version (mi.m)) - os << "if (sk == statement_select)" - << "{"; - // If the whole class is readonly, then we will never be - // called with sk == statement_update. - // - else if (!readonly (*context::top_object)) - { - semantics::class_* c; - - if (id (mi.m) || - readonly (mi.m) || - ((c = composite (mi.t)) && readonly (*c))) - os << "if (sk != statement_update)" - << "{"; - } - } - - return true; - } - - virtual void - post (member_info& mi) - { - if (var_override_.empty ()) - { - semantics::class_* c; - - if ((c = composite (mi.t))) - { - bool ro (readonly (*c)); - column_count_type const& cc (column_count (*c)); - - os << "n += " << cc.total << "UL"; - - // select = total - // insert = total - inverse - // update = total - inverse - readonly - // - if (cc.inverse != 0 || (!ro && cc.readonly != 0)) - { - os << " - (" << endl - << "sk == statement_select ? 0 : "; - - if (cc.inverse != 0) - os << cc.inverse << "UL"; - - if (!ro && cc.readonly != 0) - { - if (cc.inverse != 0) - os << " + "; - - os << "(" << endl - << "sk == statement_insert ? 0 : " << - cc.readonly << "UL)"; - } - - os << ")"; - } - - os << ";"; - } - else - os << "n++;"; - - bool block (false); - - // The same logic as in pre(). - // - if (inverse (mi.m, key_prefix_) || version (mi.m)) - block = true; - else if (!readonly (*context::top_object)) - { - semantics::class_* c; - - if (id (mi.m) || - readonly (mi.m) || - ((c = composite (mi.t)) && readonly (*c))) - block = true; - } - - if (block) - os << "}"; - else - os << endl; - } - } - - virtual void - traverse_composite (member_info& mi) - { - os << "composite_value_traits< " << mi.fq_type () << - " >::bind (b + n, " << arg << "." << mi.var << "value, sk);"; - } - virtual void traverse_integer (member_info& mi) { @@ -179,10 +69,6 @@ namespace relational "value.capacity ();" << b << ".is_null = &" << arg << "." << mi.var << "null;"; } - - private: - string b; - string arg; }; entry bind_member_; diff --git a/odb/semantics/class.hxx b/odb/semantics/class.hxx index d62de53..b9f8f34 100644 --- a/odb/semantics/class.hxx +++ b/odb/semantics/class.hxx @@ -111,7 +111,7 @@ namespace semantics } using scope::add_edge_left; - using scope::add_edge_right; + using type::add_edge_right; // Resolve conflict between scope::scope and nameable::scope. // diff --git a/odb/semantics/elements.cxx b/odb/semantics/elements.cxx index 85fb203..4b8fcea 100644 --- a/odb/semantics/elements.cxx +++ b/odb/semantics/elements.cxx @@ -322,8 +322,25 @@ namespace semantics void scope:: add_edge_left (names& e) { - names_list::iterator it (names_.insert (names_.end (), &e)); - iterator_map_[&e] = it; + names_list::iterator i (names_.insert (names_.end (), &e)); + iterator_map_[&e] = i; + names_map_[e.name ()].push_back (&e); + } + + void scope:: + add_edge_left (names& e, names_iterator after) + { + names_list::iterator i; + + if (after.base () == names_.end ()) + i = names_.insert (names_.begin (), &e); + else + { + names_list::iterator j (after.base ()); + i = names_.insert (++j, &e); + } + + iterator_map_[&e] = i; names_map_[e.name ()].push_back (&e); } @@ -426,6 +443,14 @@ namespace semantics insert (ti); } + // virtual_data_member + // + { + type_info ti (typeid (virtual_data_member)); + ti.add_base (typeid (data_member)); + insert (ti); + } + // unsupported_type // { diff --git a/odb/semantics/elements.hxx b/odb/semantics/elements.hxx index ef28d23..59e058d 100644 --- a/odb/semantics/elements.hxx +++ b/odb/semantics/elements.hxx @@ -67,6 +67,40 @@ namespace semantics class edge; class unit; + // Support for inserting edges at specified positions. + // + template + struct node_position + { + node_position (N& node, I pos) + : node_ (node), pos_ (pos) + { + } + + operator N& () const + { + return node_; + } + + template + void + add_edge_left (E& e) + { + node_.add_edge_left (e, pos_); + } + + template + void + add_edge_right (E& e) + { + node_.add_edge_right (e, pos_); + } + + private: + N& node_; + I pos_; + }; + // // class edge: public context @@ -529,6 +563,9 @@ namespace semantics void add_edge_left (names&); + void + add_edge_left (names&, names_iterator after); + using nameable::add_edge_right; protected: @@ -544,6 +581,8 @@ namespace semantics // // + class points; + class belongs; class qualifies; @@ -567,6 +606,15 @@ namespace semantics } public: + bool + pointed_p () const {return pointed_ != 0;} + + points& + pointed () const {return *pointed_;} + + public: + type (): pointed_ (0) {} + void add_edge_right (belongs&) { @@ -578,10 +626,17 @@ namespace semantics qualified_.push_back (&e); } + void + add_edge_right (points& e) + { + pointed_ = &e; + } + using nameable::add_edge_right; private: qualified qualified_; + points* pointed_; }; // @@ -688,6 +743,22 @@ namespace semantics : node (file, line, column, tn) { } + + protected: + data_member () + { + } + }; + + // Virtual data member (extension to the standard C++ model). + // + class virtual_data_member: public data_member + { + public: + virtual_data_member (path const& file, size_t line, size_t column) + : node (file, line, column, 0) + { + } }; // Unsupported type. diff --git a/odb/semantics/relational/elements.cxx b/odb/semantics/relational/elements.cxx index 7a0ec1d..a73520c 100644 --- a/odb/semantics/relational/elements.cxx +++ b/odb/semantics/relational/elements.cxx @@ -15,16 +15,14 @@ namespace semantics template <> duplicate_name:: duplicate_name (uscope& s, unameable& o, unameable& d) - : scope (s), orig (o), dup (d), - orig_name (o.name ()), dup_name (d.name ()) + : scope (s), orig (o), dup (d), name (o.name ()) { } template <> duplicate_name:: duplicate_name (qscope& s, qnameable& o, qnameable& d) - : scope (s), orig (o), dup (d), - orig_name (o.name ().string ()), dup_name (d.name ().string ()) + : scope (s), orig (o), dup (d), name (o.name ().string ()) { } diff --git a/odb/semantics/relational/elements.hxx b/odb/semantics/relational/elements.hxx index 9d98619..6b4964e 100644 --- a/odb/semantics/relational/elements.hxx +++ b/odb/semantics/relational/elements.hxx @@ -212,8 +212,7 @@ namespace semantics node& orig; node& dup; - string orig_name; - string dup_name; + string name; }; template diff --git a/odb/semantics/unit.hxx b/odb/semantics/unit.hxx index 11c8a8a..ca001b1 100644 --- a/odb/semantics/unit.hxx +++ b/odb/semantics/unit.hxx @@ -37,6 +37,8 @@ namespace semantics tree_node_map_[key] = &value; } + using namespace_::find; + // Mapping from tree nodes to name hints. // public: diff --git a/odb/traversal/elements.cxx b/odb/traversal/elements.cxx index 34955d9..7ba207d 100644 --- a/odb/traversal/elements.cxx +++ b/odb/traversal/elements.cxx @@ -75,4 +75,24 @@ namespace traversal { d.dispatch (m.belongs ()); } + + // virtual_data_member + // + void virtual_data_member:: + traverse (type& m) + { + belongs (m); + } + + void virtual_data_member:: + belongs (type& m) + { + belongs (m, *this); + } + + void virtual_data_member:: + belongs (type& m, edge_dispatcher& d) + { + d.dispatch (m.belongs ()); + } } diff --git a/odb/traversal/elements.hxx b/odb/traversal/elements.hxx index ad9f4e9..967858f 100644 --- a/odb/traversal/elements.hxx +++ b/odb/traversal/elements.hxx @@ -233,6 +233,20 @@ namespace traversal // // + struct virtual_data_member: node + { + virtual void + traverse (type&); + + virtual void + belongs (type&); + + virtual void + belongs (type&, edge_dispatcher&); + }; + + // + // struct unsupported_type: node {}; } diff --git a/odb/validator.cxx b/odb/validator.cxx index c051487..d346b25 100644 --- a/odb/validator.cxx +++ b/odb/validator.cxx @@ -73,7 +73,7 @@ namespace // Make sure id or inverse member is not marked readonly since we // depend on these three sets not having overlaps. // - if (readonly (m)) + if (m.count ("readonly")) // context::readonly() also checks the class. { if (id (m)) { @@ -172,7 +172,9 @@ namespace { if (id (m)) { - if (id_ != 0) + if (id_ == 0) + id_ = &m; + else { os << m.file () << ":" << m.line () << ":" << m.column () << ":" << " error: multiple object id members" << endl; @@ -182,13 +184,13 @@ namespace valid_ = false; } - else - id_ = &m; } if (version (m)) { - if (optimistic_ != 0) + if (optimistic_ == 0) + optimistic_ = &m; + else { os << m.file () << ":" << m.line () << ":" << m.column () << ":" << " error: multiple version members" << endl; @@ -200,8 +202,6 @@ namespace valid_ = false; } - else - optimistic_ = &m; } } @@ -305,6 +305,7 @@ namespace // Check bases. // bool base (false); + type* poly_root (0); for (type::inherits_iterator i (c.inherits_begin ()); i != c.inherits_end (); @@ -313,7 +314,37 @@ namespace type& b (i->base ()); if (object (b)) + { base = true; + + if (type* r = polymorphic (b)) + { + if (poly_root == 0) + { + poly_root = r; + c.set ("polymorphic-base", &static_cast (b)); + } + // If poly_root and r are the same, then we have virtual + // inheritance. Though we don't support it at the moment. + // + else //if (poly_root != r) + { + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: persistent class '" << class_name (c) << "' " + << "derives from multiple polymorphic bases" << endl; + + type& a (*poly_root); + os << a.file () << ":" << a.line () << ":" << a.column () << ":" + << " info: first polymorphic base defined here" << endl; + + type& b (*r); + os << b.file () << ":" << b.line () << ":" << b.column () << ":" + << " info: second polymorphic base defined here" << endl; + + valid_ = false; + } + } + } else if (view (b) || composite (b)) { // @@ Should we use hint here? @@ -347,8 +378,9 @@ namespace if (id == 0) { - // An object without an id should either be abstract or explicitly - // marked as such. + // An object without an id should either be reuse-abstract + // or explicitly marked as such. We check that it is not + // polymorphic below. // if (!(c.count ("id") || abstract (c))) { @@ -479,6 +511,36 @@ namespace } } + // Polymorphic inheritance. + // + if (c.count ("polymorphic") && poly_root == 0) + { + // Root of the hierarchy. + // + + if (id == 0) + { + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: polymorphic class without an object id" << endl; + + valid_ = false; + } + + if (!TYPE_POLYMORPHIC_P (c.tree_node ())) + { + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: non-polymorphic class (class without virtual " + << "functions) cannot be declared polymorphic" << endl; + + valid_ = false; + } + + poly_root = &c; + } + + if (poly_root != 0) + c.set ("polymorphic-root", poly_root); + // Check members. // member_.count_ = 0; @@ -491,6 +553,18 @@ namespace valid_ = false; } + + // Update features set based on this object. + // + if (class_file (c) == unit.file ()) + { + if (poly_root != 0) + features.polymorphic_object = true; + else if (id == 0 && !abstract (c)) + features.no_id_object = true; + else + features.simple_object = true; + } } virtual void @@ -512,6 +586,34 @@ namespace valid_ = false; } + // Check that the callback function exist. + // + if (c.count ("callback")) + { + string name (c.get ("callback")); + tree decl ( + lookup_qualified_name ( + c.tree_node (), get_identifier (name.c_str ()), false, false)); + + if (decl == error_mark_node || TREE_CODE (decl) != BASELINK) + { + os << c.file () << ":" << c.line () << ":" << c.column () << ": " + << "error: unable to resolve member function '" << name << "' " + << "specified with '#pragma db callback' for class '" + << class_name (c) << "'" << endl; + + valid_ = false; + } + + // No const version for views. + + //@@ Would be nice to check the signature of the function(s) + // instead of postponing it until the C++ compilation. Though + // we may still get C++ compilation errors because of const + // mismatch. + // + } + // Check bases. // for (type::inherits_iterator i (c.inherits_begin ()); @@ -584,6 +686,13 @@ namespace valid_ = false; } + + // Update features set based on this view. + // + if (class_file (c) == unit.file ()) + { + features.view = true; + } } virtual void @@ -1068,11 +1177,12 @@ namespace void validator:: validate (options const& ops, + features& f, semantics::unit& u, semantics::path const&, unsigned short pass) { - auto_ptr ctx (create_context (cerr, u, ops, 0)); + auto_ptr ctx (create_context (cerr, u, ops, f, 0)); bool valid (true); diff --git a/odb/validator.hxx b/odb/validator.hxx index ad9cb32..090cf86 100644 --- a/odb/validator.hxx +++ b/odb/validator.hxx @@ -6,6 +6,7 @@ #define ODB_VALIDATOR_HXX #include +#include #include class validator @@ -17,6 +18,7 @@ public: // void validate (options const&, + features&, semantics::unit&, semantics::path const&, unsigned short pass); -- cgit v1.1