From 3a1eed21d4d5d0e7f6a9f400420fdc28d7be9b61 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 17 Feb 2012 10:08:18 +0200 Subject: Add support for composite object ids New pragma id_type (member). New test: common/composite-id. The composite example has also been updated. --- odb/common.cxx | 254 +++++---- odb/common.hxx | 176 +++++- odb/context.cxx | 70 ++- odb/context.hxx | 17 + odb/gcc.hxx | 1 + odb/odb.cxx | 20 + odb/pragma.cxx | 13 +- odb/relational/common.cxx | 259 ++++++--- odb/relational/common.hxx | 189 ++++++- odb/relational/common.txx | 112 ++++ odb/relational/header.hxx | 41 +- odb/relational/model.hxx | 328 ++++++----- odb/relational/mssql/common.cxx | 103 +--- odb/relational/mssql/common.hxx | 132 +---- odb/relational/mssql/context.cxx | 17 +- odb/relational/mssql/context.hxx | 8 +- odb/relational/mssql/model.cxx | 2 +- odb/relational/mssql/source.cxx | 343 +----------- odb/relational/mysql/common.cxx | 103 +--- odb/relational/mysql/common.hxx | 118 +--- odb/relational/mysql/context.cxx | 17 +- odb/relational/mysql/context.hxx | 7 +- odb/relational/mysql/model.cxx | 2 +- odb/relational/mysql/source.cxx | 361 ++---------- odb/relational/oracle/common.cxx | 101 +--- odb/relational/oracle/common.hxx | 118 +--- odb/relational/oracle/context.cxx | 17 +- odb/relational/oracle/context.hxx | 8 +- odb/relational/oracle/model.cxx | 4 +- odb/relational/oracle/source.cxx | 356 ++---------- odb/relational/pgsql/common.cxx | 101 +--- odb/relational/pgsql/common.hxx | 118 +--- odb/relational/pgsql/context.cxx | 17 +- odb/relational/pgsql/context.hxx | 8 +- odb/relational/pgsql/model.cxx | 2 +- odb/relational/pgsql/source.cxx | 436 +++------------ odb/relational/processor.cxx | 147 +++-- odb/relational/source.hxx | 1096 ++++++++++++++++++++++++++++--------- odb/relational/sqlite/common.cxx | 103 +--- odb/relational/sqlite/common.hxx | 118 +--- odb/relational/sqlite/context.cxx | 17 +- odb/relational/sqlite/context.hxx | 8 +- odb/relational/sqlite/model.cxx | 2 +- odb/relational/sqlite/source.cxx | 340 +----------- odb/validator.cxx | 583 +++++++++++++------- 45 files changed, 2867 insertions(+), 3526 deletions(-) create mode 100644 odb/relational/common.txx (limited to 'odb') diff --git a/odb/common.cxx b/odb/common.cxx index 230e674..6261b85 100644 --- a/odb/common.cxx +++ b/odb/common.cxx @@ -16,6 +16,12 @@ traverse_simple (semantics::data_member&) } void object_members_base:: +traverse_pointer (semantics::data_member& m, semantics::class_& c) +{ + traverse_member (m, utype (*id_member (c))); +} + +void object_members_base:: traverse_composite (semantics::data_member*, semantics::class_& c) { inherits (c); @@ -51,26 +57,6 @@ traverse_view (semantics::class_& c) } void object_members_base:: -traverse (semantics::data_member& m, semantics::class_& c) -{ - // We are starting from the member. Add an empty chain which - // corresponds to the scope that contains this member. - // - //member_scope_.push_back (class_inheritance_chain ()); - //member_path_.push_back (&m); - - member_scope_.push_back (class_inheritance_chain ()); - member_scope_.back ().push_back (&c); - - traverse_composite_wrapper (&m, c, 0); - - member_scope_.pop_back (); - - //member_path_.pop_back (); - //member_scope_.pop_back (); -} - -void object_members_base:: traverse (semantics::class_& c) { class_kind_type k (class_kind (c)); @@ -156,68 +142,73 @@ traverse (semantics::class_& c) context::cur_object = prev; } -void object_members_base::member:: -traverse (semantics::data_member& m) +void object_members_base:: +traverse_member (semantics::data_member& m, semantics::type& t) { - if (transient (m)) - return; - - om_.member_path_.push_back (&m); - - semantics::type& t (utype (m)); - if (semantics::class_* comp = context::composite_wrapper (t)) { - om_.member_scope_.push_back (class_inheritance_chain ()); - om_.member_scope_.back ().push_back (comp); + member_scope_.push_back (class_inheritance_chain ()); + member_scope_.back ().push_back (comp); qname old_table_prefix; string old_flat_prefix, old_member_prefix; - if (om_.build_flat_prefix_) + if (build_flat_prefix_) { - old_flat_prefix = om_.flat_prefix_; - om_.flat_prefix_ += om_.public_name (m); - om_.flat_prefix_ += '_'; + old_flat_prefix = flat_prefix_; + flat_prefix_ += public_name (m); + flat_prefix_ += '_'; } - if (om_.build_member_prefix_) + if (build_member_prefix_) { - old_member_prefix = om_.member_prefix_; - om_.member_prefix_ += m.name (); - om_.member_prefix_ += '.'; + old_member_prefix = member_prefix_; + member_prefix_ += m.name (); + member_prefix_ += '.'; } - if (om_.build_table_prefix_) + if (build_table_prefix_) { - old_table_prefix = om_.table_prefix_.prefix; - append (m, om_.table_prefix_); + old_table_prefix = table_prefix_.prefix; + append (m, table_prefix_); } - om_.traverse_composite_wrapper (&m, *comp, (wrapper (t) ? &t : 0)); + traverse_composite_wrapper (&m, *comp, (wrapper (t) ? &t : 0)); - if (om_.build_table_prefix_) + if (build_table_prefix_) { - om_.table_prefix_.level--; - om_.table_prefix_.prefix = old_table_prefix; + table_prefix_.level--; + table_prefix_.prefix = old_table_prefix; } - if (om_.build_flat_prefix_) - om_.flat_prefix_ = old_flat_prefix; + if (build_flat_prefix_) + flat_prefix_ = old_flat_prefix; - if (om_.build_member_prefix_) - om_.member_prefix_ = old_member_prefix; + if (build_member_prefix_) + member_prefix_ = old_member_prefix; - om_.member_scope_.pop_back (); + member_scope_.pop_back (); } - else if (semantics::type* c = context::container (m)) - { + else + traverse_simple (m); +} + +void object_members_base::member:: +traverse (semantics::data_member& m) +{ + if (transient (m)) + return; + + om_.member_path_.push_back (&m); + + semantics::type& t (utype (m)); + + if (semantics::type* c = context::container (m)) om_.traverse_container (m, *c); - } + else if (semantics::class_* c = object_pointer (t)) + om_.traverse_pointer (m, *c); else - { - om_.traverse_simple (m); - } + om_.traverse_member (m, t); om_.member_path_.pop_back (); } @@ -283,6 +274,18 @@ flush () { } +bool object_columns_base:: +traverse_column (semantics::data_member&, string const&, bool) +{ + return false; +} + +void object_columns_base:: +traverse_pointer (semantics::data_member& m, semantics::class_& c) +{ + traverse_member (m, utype (*id_member (c))); +} + void object_columns_base:: traverse_composite (semantics::data_member*, semantics::class_& c) { @@ -307,30 +310,39 @@ traverse_view (semantics::class_& c) void object_columns_base:: traverse (semantics::data_member& m, - semantics::class_& c, - string const& key_prefix, - string const& default_name) + semantics::type& t, + std::string const& kp, + std::string const& dn, + semantics::class_* to) { - // We are starting from the member. Add an empty chain which - // corresponds to the scope that contains this member. - // - //member_scope_.push_back (class_inheritance_chain ()); - //member_path_.push_back (&m); + semantics::class_* oto (context::top_object); - member_scope_.push_back (class_inheritance_chain ()); - member_scope_.back ().push_back (&c); + if (to != 0) + context::top_object = to; - column_prefix_ = column_prefix (m, key_prefix, default_name); + semantics::class_* c (object_pointer (t)); + semantics::type* rt (c == 0 ? &t : &utype (*id_member (*c))); - traverse_composite (&m, c); + root_ = &m; + root_id_ = (kp.empty () ? context::id (m) : kp == "id"); + root_op_ = (c != 0); - if (!member_.first_) - flush (); + key_prefix_ = kp; + default_name_ = dn; - member_scope_.pop_back (); + if (root_op_) + traverse_pointer (m, *c); + else + traverse_member (m, *rt); + + key_prefix_.clear (); + default_name_.clear (); + + if (!first_ && composite_wrapper (*rt)) + flush (); - //member_path_.pop_back (); - //member_scope_.pop_back (); + root_ = 0; + context::top_object = oto; } void object_columns_base:: @@ -386,7 +398,7 @@ traverse (semantics::class_& c) context::cur_object = prev; } - if (f && !member_.first_) + if (f && !first_) flush (); } @@ -421,48 +433,102 @@ column_prefix (semantics::data_member& m, string const& kp, string const& dn) return r; } -void object_columns_base::member:: -traverse (semantics::data_member& m) +string object_columns_base:: +column_prefix (data_member_path const& mp) { - if (transient (m)) - return; + if (mp.size () < 2) + return ""; - oc_.member_path_.push_back (&m); + string r; - semantics::type& t (utype (m)); + for (data_member_path::const_iterator i (mp.begin ()), e (mp.end () - 1); + i != e; ++i) + r += column_prefix (**i); + + return r; +} +void object_columns_base:: +traverse_member (semantics::data_member& m, semantics::type& t) +{ if (semantics::class_* comp = composite_wrapper (t)) { - oc_.member_scope_.push_back (class_inheritance_chain ()); - oc_.member_scope_.back ().push_back (comp); + member_scope_.push_back (class_inheritance_chain ()); + member_scope_.back ().push_back (comp); - string old_prefix (oc_.column_prefix_); - oc_.column_prefix_ += column_prefix (m); + string old_prefix (column_prefix_); + column_prefix_ += column_prefix (m, key_prefix_, default_name_); - oc_.traverse_composite (&m, *comp); + // Save and clear the key prefix and default name. + // + string old_kp, old_dn; + old_kp.swap (key_prefix_); + old_dn.swap (default_name_); - oc_.column_prefix_ = old_prefix; + traverse_composite (&m, *comp); - oc_.member_scope_.pop_back (); - } - else if (container (m)) - { - // Container gets its own table, so nothing to do here. - // + old_kp.swap (key_prefix_); + old_dn.swap (default_name_); + + column_prefix_ = old_prefix; + member_scope_.pop_back (); } else { - if (oc_.traverse_column (m, oc_.column_prefix_ + column_name (m), first_)) + string name (column_prefix_ + column_name (m, key_prefix_, default_name_)); + + if (traverse_column (m, name, first_)) { if (first_) first_ = false; } } +} + +void object_columns_base::member:: +traverse (semantics::data_member& m) +{ + if (transient (m)) + return; + + // Container gets its own table, so nothing to do here. + // + if (container (m)) + return; + + oc_.member_path_.push_back (&m); + + semantics::type& t (utype (m)); + + if (semantics::class_* c = object_pointer (t)) + oc_.traverse_pointer (m, *c); + else + oc_.traverse_member (m, t); oc_.member_path_.pop_back (); } // +// object_columns_list +// + +void object_columns_list:: +traverse_pointer (semantics::data_member& m, semantics::class_& c) +{ + // Ignore inverse object pointers. + // + if (!ignore_inverse_ || !inverse (m, key_prefix_)) + object_columns_base::traverse_pointer (m, c); +} + +bool object_columns_list:: +traverse_column (semantics::data_member& m, std::string const& name, bool) +{ + columns_.push_back (column (name, column_type (), m)); + return true; +} + +// // typedefs // diff --git a/odb/common.hxx b/odb/common.hxx index 28ed2c1..ffbd2d6 100644 --- a/odb/common.hxx +++ b/odb/common.hxx @@ -6,7 +6,9 @@ #define ODB_COMMON_HXX #include +#include #include // std::size_t +#include #include @@ -18,6 +20,14 @@ struct object_members_base: traversal::class_, virtual context virtual void traverse_simple (semantics::data_member&); + // Traverse object pointer. The second argument is the pointed-to + // class. When overriding this function, you will most likely want + // to call the base in order to traverse the pointer as a simple + // member (simple object id) or as composite (composite object id). + // + virtual void + traverse_pointer (semantics::data_member&, semantics::class_&); + // If you override this function, you can call the base to traverse // bases and members. The first argument is the data member and can // be NULL if we are traversing the root type or a base. The second @@ -81,11 +91,6 @@ public: virtual void traverse (semantics::class_&); - // Composite value with data member. - // - virtual void - traverse (semantics::data_member&, semantics::class_& comp); - public: // Append composite member prefix. // @@ -93,13 +98,21 @@ public: append (semantics::data_member&, table_prefix&); protected: - std::string flat_prefix_; + string flat_prefix_; table_prefix table_prefix_; - std::string member_prefix_; + string member_prefix_; data_member_path member_path_; data_member_scope member_scope_; +protected: + semantics::data_member* + id () const + { + assert (!member_path_.empty ()); + return context::id (member_path_); + } + private: void init (bool build_flat_prefix, @@ -115,6 +128,9 @@ private: } private: + virtual void + traverse_member (semantics::data_member&, semantics::type&); + struct member: traversal::data_member { member (object_members_base& om) @@ -148,8 +164,16 @@ struct object_columns_base: traversal::class_, virtual context // virtual bool traverse_column (semantics::data_member&, - std::string const& name, - bool first) = 0; + string const& name, + bool first); + + // Traverse object pointer. The second argument is the pointed-to + // class. When overriding this function, you will most likely want + // to call the base in order to traverse the member as a column + // (simple object id) or columns (composite object id). + // + virtual void + traverse_pointer (semantics::data_member&, semantics::class_&); // If you override this function, you can call the base to traverse // bases and members. The first argument is the data member and can @@ -179,14 +203,22 @@ struct object_columns_base: traversal::class_, virtual context flush (); public: - object_columns_base () - : top_level_ (true), member_ (*this) + object_columns_base (bool first = true, + string const& column_prefix = string ()) + : column_prefix_ (column_prefix), + root_ (0), + first_ (first), + top_level_ (true), + member_ (*this) { init (); } - object_columns_base (object_columns_base const&) + object_columns_base (object_columns_base const& x) : context (), //@@ -Wextra + column_prefix_ (x.column_prefix_), + root_ (0), + first_ (x.first_), top_level_ (true), member_ (*this) { @@ -196,28 +228,76 @@ public: virtual void traverse (semantics::class_&); - // Composite value with data member. + // Traverse a data member with type, which can be a simple or composite + // value type, or an object pointer (with a simple or composite id). // virtual void + traverse (semantics::data_member& m) + { + traverse (m, utype (m), string (), string ()); + } + + virtual void traverse (semantics::data_member&, - semantics::class_& comp, - std::string const& key_prefix, - std::string const& default_name); + semantics::type&, + string const& key_prefix, + string const& default_name, + semantics::class_* top_object = 0); // If not 0, switch top object. public: // Return column prefix for composite data member. // static string column_prefix (semantics::data_member&, - std::string const& key_prefix = std::string (), - std::string const& default_name = std::string ()); + string const& key_prefix = string (), + string const& default_name = string ()); + + // Return column prefix up to (but not including) the last member + // in the path. + // + static string + column_prefix (data_member_path const&); protected: + string key_prefix_; + string default_name_; + string column_prefix_; data_member_path member_path_; data_member_scope member_scope_; +protected: + semantics::data_member* + id () const + { + if (root_ != 0) + return root_id_ ? root_ : 0; // Cannot have ids below root. + else + { + assert (!member_path_.empty ()); + return context::id (member_path_); + } + } + + string + column_type () + { + if (member_path_.empty ()) + { + assert (root_ != 0); + return context::column_type (*root_, key_prefix_); + } + else + return context::column_type ( + member_path_, key_prefix_, (root_ != 0 && (root_id_ || root_op_))); + } + +private: + semantics::data_member* root_; // Root member if traversing from a member. + bool root_id_; // True if traversing root as object id. + bool root_op_; // True if traversing root as object pointer. + private: void init () @@ -227,21 +307,21 @@ private: } private: + virtual void + traverse_member (semantics::data_member&, semantics::type&); + struct member: traversal::data_member, context { - member (object_columns_base& oc) - : oc_ (oc), first_ (true) - { - } + member (object_columns_base& oc): oc_ (oc) {} virtual void traverse (semantics::data_member&); public: object_columns_base& oc_; - bool first_; }; + bool first_; bool top_level_; member member_; @@ -249,6 +329,56 @@ private: traversal::inherits inherits_; }; +struct object_columns_list: object_columns_base +{ + object_columns_list (bool ignore_inverse = true) + : ignore_inverse_ (ignore_inverse) + { + } + + object_columns_list (string const& column_prefix, bool ignore_inverse = true) + : object_columns_base (true, column_prefix), + ignore_inverse_ (ignore_inverse) + { + } + + struct column + { + column (std::string const& n, + std::string const& t, + semantics::data_member& m) + : name (n), type (t), member (&m) + { + } + + std::string name; + std::string type; + semantics::data_member* member; + }; + + typedef std::vector columns; + typedef columns::const_iterator iterator; + + iterator + begin () const {return columns_.begin ();} + + iterator + end () const {return columns_.end ();} + + columns::size_type + size () const {return columns_.size ();} + + virtual void + traverse_pointer (semantics::data_member&, semantics::class_&); + + virtual bool + traverse_column (semantics::data_member&, string const&, bool); + +private: + bool ignore_inverse_; + columns columns_; +}; + // Traverse composite values that are class template instantiations. // struct typedefs: traversal::typedefs, context diff --git a/odb/context.cxx b/odb/context.cxx index f4fa651..15579f3 100644 --- a/odb/context.cxx +++ b/odb/context.cxx @@ -210,6 +210,32 @@ context () context* context::current_; +semantics::data_member* context:: +id (data_member_path const& mp) +{ + for (data_member_path::const_reverse_iterator i (mp.rbegin ()); + i != mp.rend (); ++i) + { + if (id (**i)) + return *i; + } + + return 0; +} + +semantics::data_member* context:: +object_pointer (data_member_path const& mp) +{ + for (data_member_path::const_reverse_iterator i (mp.rbegin ()); + i != mp.rend (); ++i) + { + if (object_pointer (utype (**i))) + return *i; + } + + return 0; +} + bool context:: readonly (data_member_path const& mp, data_member_scope const& ms) { @@ -767,6 +793,9 @@ column_name (data_member_path const& mp) const string context:: column_name (semantics::data_member& m, string const& p, string const& d) const { + if (p.empty () && d.empty ()) + return column_name (m); + // A container column name can be specified for the member or for the // container type. // @@ -786,6 +815,23 @@ column_name (semantics::data_member& m, string const& p, string const& d) const } string context:: +column_type (const data_member_path& mp, string const& kp, bool id) +{ + if (kp.empty ()) + { + // Return the id type if this member is or is a part of an object id + // or pointer to object. + // + return mp.back ()->get ( + id || context::id (mp) || object_pointer (mp) + ? "column-id-type" + : "column-type"); + } + else + return indirect_value (*mp.back (), kp + "-column-type"); +} + +string context:: column_type (semantics::data_member& m, string const& kp) { return kp.empty () @@ -1221,14 +1267,23 @@ namespace struct column_count_impl: object_members_base { virtual void + traverse_pointer (semantics::data_member& m, semantics::class_& c) + { + size_t t (c_.total); + + object_members_base::traverse_pointer (m, c); + + if (context::inverse (m)) + c_.inverse += (c_.total - t); + } + + virtual void traverse_simple (semantics::data_member& m) { c_.total++; - if (m.count ("id")) + if (id ()) c_.id++; - else if (context::inverse (m)) - c_.inverse++; else if (context::readonly (member_path_, member_scope_)) c_.readonly++; else if (context::version (m)) @@ -1268,6 +1323,15 @@ namespace } virtual void + traverse_pointer (semantics::data_member&, semantics::class_&) + { + if (context::is_a (member_path_, member_scope_, flags_)) + r_++; + + // No need to go inside. + } + + virtual void traverse_simple (semantics::data_member&) { if (context::is_a (member_path_, member_scope_, flags_)) diff --git a/odb/context.hxx b/odb/context.hxx index e67af48..7e62161 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -364,6 +364,12 @@ public: return t.get ("element-type", 0); } + // If this data member is or is part of an object pointer, then + // return the member that is the pointer. Otherwise, return 0. + // + static semantics::data_member* + object_pointer (data_member_path const&); + static bool abstract (semantics::class_& c) { @@ -385,6 +391,12 @@ public: return m.count ("id"); } + // If this data member is or is part of an id member, then return + // the member that is marked as the id. Otherwise, return 0. + // + static semantics::data_member* + id (data_member_path const&); + static bool auto_ (semantics::data_member& m) { @@ -500,6 +512,11 @@ public: string const& default_name) const; string + column_type (const data_member_path&, + string const& key_prefix = string (), + bool id = false); // Pass true if this type is object id other + // than because of the members in the path. + string column_type (semantics::data_member&, string const& key_prefix = string ()); string diff --git a/odb/gcc.hxx b/odb/gcc.hxx index a9da1bd..da77027 100644 --- a/odb/gcc.hxx +++ b/odb/gcc.hxx @@ -42,6 +42,7 @@ extern "C" #endif #include +#include } #ifndef LOCATION_COLUMN diff --git a/odb/odb.cxx b/odb/odb.cxx index dd9b180..b17369a 100644 --- a/odb/odb.cxx +++ b/odb/odb.cxx @@ -692,6 +692,26 @@ main (int argc, char* argv[]) << "#endif" << endl << endl; + // Add ODB compiler metaprogramming tests. + // + os << "namespace odb" << endl + << "{" << endl + << "namespace compiler" << endl + << "{" << endl; + + // operator< test, used in validator. + // + os << "template " << endl + << "bool" << endl + << "has_lt_operator (T const& x, T const& y)" << endl + << "{" << endl + << "bool r (x < y);" << endl + << "return r;" << endl + << "}" << endl; + + os << "}" << endl + << "}" << endl; + // Add custom prologue if any. // // NOTE: if you change the format, you also need to update code diff --git a/odb/pragma.cxx b/odb/pragma.cxx index 5149bee..6c92f0f 100644 --- a/odb/pragma.cxx +++ b/odb/pragma.cxx @@ -493,18 +493,8 @@ check_spec_decl_type (tree d, return false; } } - else if (p == "id_type") - { - // Id type can only be used for types. - // - if (!TYPE_P (d)) - { - error (l) << "name '" << name << "' in db pragma " << p << " does " - << "not refer to a type" << endl; - return false; - } - } else if (p == "type" || + p == "id_type" || p == "value_type" || p == "index_type" || p == "key_type") @@ -1841,6 +1831,7 @@ handle_pragma_qualifier (cpp_reader* reader, string const& p) p == "key_options" || p == "id_options" || p == "type" || + p == "id_type" || p == "value_type" || p == "index_type" || p == "key_type" || diff --git a/odb/relational/common.cxx b/odb/relational/common.cxx index bf325f7..381d77f 100644 --- a/odb/relational/common.cxx +++ b/odb/relational/common.cxx @@ -78,16 +78,11 @@ namespace relational } } - bool query_columns_base:: - traverse_column (semantics::data_member& m, string const& column, bool) + void query_columns_base:: + traverse_pointer (semantics::data_member& m, semantics::class_& c) { - semantics::class_* ptr (object_pointer (utype (m))); - - if (ptr == 0) - return false; - string name (public_name (m)); - bool inv (inverse (m)); + bool inv (inverse (m, key_prefix_)); if (decl_) { @@ -101,7 +96,7 @@ namespace relational os << "typedef" << endl << "odb::query_pointer<" << endl << " odb::pointer_query_columns<" << endl - << " " << class_fq_name (*ptr) << "," << endl + << " " << class_fq_name (c) << "," << endl << " " << name << "_alias_ > >" << endl << name << "_type_ ;" << endl @@ -111,11 +106,32 @@ namespace relational } else { - // For now use column name as table alias. - // @@ This will become problematic when we add support for composite ids. + // 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_); + os << "const char " << scope_ << "::" << name << "_alias_[] = " << - strlit (quote_id (column)) << ";" + strlit (quote_id (alias)) << ";" << endl; if (inv) @@ -123,8 +139,6 @@ namespace relational << scope_ << "::" << name << ";" << endl; } - - return true; } // query_columns @@ -132,13 +146,13 @@ namespace relational query_columns:: query_columns (bool ptr) - : ptr_ (ptr), decl_ (true) + : ptr_ (ptr), decl_ (true), in_ptr_ (false) { } query_columns:: query_columns (bool ptr, semantics::class_& c) //@@ context::{cur,top}_object - : ptr_ (ptr), decl_ (false) + : ptr_ (ptr), decl_ (false), in_ptr_ (false) { scope_ = ptr ? "pointer_query_columns" : "query_columns"; scope_ += "< " + class_fq_name (c) + ", table >"; @@ -164,12 +178,13 @@ namespace relational } string name (public_name (*m)); + string suffix (in_ptr_ ? "_column_type_" : "_type_"); if (decl_) { os << "// " << name << endl << "//" << endl - << "struct " << name << "_type_"; + << "struct " << name << suffix; // Derive from the base in query_columns_base. It contains columns // data for the pointer members. @@ -178,27 +193,29 @@ namespace relational os << ": " << name << "_base_"; os << "{" - << name << "_type_ (){}"; // For some reason GCC needs this c-tor - // if we make the static member const. + << name << suffix << " (){}"; // For some reason GCC needs this c-tor + // if we make the static member const. object_columns_base::traverse_composite (m, c); - os << "};" - << "static const " << name << "_type_ " << name << ";" - << endl; + os << "};"; + + if (!in_ptr_) + os << "static const " << name << "_type_ " << name << ";" + << endl; } else { // Handle nested members first. // string old_scope (scope_); - scope_ += "::" + name + "_type_"; + scope_ += "::" + name + suffix; object_columns_base::traverse_composite (m, c); scope_ = old_scope; - // Composite member. + // Composite member. Note that here we don't use suffix. // os << "template " << endl << "const typename " << scope_ << "::" << name << "_type_" << endl @@ -216,39 +233,16 @@ namespace relational << "}"; } - bool query_columns:: - traverse_column (semantics::data_member& m, string const& column, bool) + void query_columns:: + column_common (semantics::data_member& m, + string const& type, + string const& column, + string const& suffix) { - semantics::names* hint; - semantics::type& t (utype (m, hint)); - semantics::class_* ptr (object_pointer (t)); - - if (ptr != 0) - { - // 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 - // compare ids. In case this is for query_columns, then for the - // inverse member everything has been generated in query_columns_base. - // - if (inverse (m)) - return false; - } - string name (public_name (m)); if (decl_) { - string type; - if (ptr != 0) - { - semantics::data_member& id (*id_member (*ptr)); - semantics::type& t (utype (id, hint)); - type = t.fq_name (hint); - } - else - type = t.fq_name (hint); - string type_id (database_type_id (m)); os << "// " << name << endl @@ -259,43 +253,14 @@ namespace relational << " " << db << "::value_traits<" << endl << " " << type << "," << endl << " " << type_id << " >::query_type," << endl - << " " << type_id << " >" << endl; - - if (ptr == 0 || ptr_) - os << name << "_type_;" - << endl; - else - { - os << name << "_column_type_;" - << endl - << "typedef" << endl - << "odb::query_pointer<" << endl - << " odb::pointer_query_columns<" << endl - << " " << class_fq_name (*ptr) << "," << endl - << " " << name << "_alias_ > >" << endl - << name << "_pointer_type_;" - << endl; - - // If this is a non-inverse relationship, then make the column have - // a dual interface: that of an object pointer and of an id column. - // The latter allows the user to, for example, use the is_null() - // test in a natural way. For inverse relationships there is no - // column and so the column interface is not available. - // - os << "struct " << name << "_type_: " << - name << "_pointer_type_, " << name << "_column_type_" - << "{"; - - column_ctor (name + "_type_", name + "_column_type_"); - - os << "};"; - } - - os << "static const " << name << "_type_ " << name << ";" + << " " << type_id << " >" << endl + << name << suffix << ";" << endl; } else { + // Note that here we don't use suffix. + // os << "template " << endl << "const typename " << scope_ << "::" << name << "_type_" << endl << scope_ << "::" << name << " (" << "table, " << @@ -306,10 +271,134 @@ namespace relational os << ");" << endl; } + } + + bool query_columns:: + traverse_column (semantics::data_member& m, string const& column, bool) + { + semantics::names* hint; + semantics::type& t (utype (m, hint)); + + column_common (m, t.fq_name (hint), column); + + if (decl_) + { + string name (public_name (m)); + + os << "static const " << name << "_type_ " << name << ";" + << endl; + } return true; } + void query_columns:: + traverse_pointer (semantics::data_member& m, semantics::class_& c) + { + // 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 + // compare ids. In case this is for query_columns, then for the + // inverse member everything has been generated in query_columns_base. + // + if (inverse (m, key_prefix_)) + return; + + string name (public_name (m)); + + semantics::data_member& id (*id_member (c)); + semantics::names* hint; + semantics::type& t (utype (id, hint)); + + if (composite_wrapper (t)) + { + // Composite id. + // + + // For pointer_query_columns generate normal composite mapping. + // + if (ptr_) + object_columns_base::traverse_pointer (m, c); + else + { + // If this is a non-inverse relationship, then make the column have + // a dual interface: that of an object pointer and of an id column. + // The latter allows the user to, for example, use the is_null() + // test in a natural way. For inverse relationships there is no + // column and so the column interface is not available. + // + in_ptr_ = true; + object_columns_base::traverse_pointer (m, c); + in_ptr_ = false; + + if (decl_) + { + os << "typedef" << endl + << "odb::query_pointer<" << endl + << " odb::pointer_query_columns<" << endl + << " " << class_fq_name (c) << "," << endl + << " " << name << "_alias_ > >" << endl + << name << "_pointer_type_;" + << endl; + + os << "struct " << name << "_type_: " << + name << "_pointer_type_, " << name << "_column_type_" + << "{" + << "};"; + + os << "static const " << name << "_type_ " << name << ";" + << endl; + } + } + } + else + { + // Simple id. + // + string type (t.fq_name (hint)); + string column (column_prefix_ + + column_name (m, key_prefix_, default_name_)); + + // For pointer_query_columns generate normal column mapping. + // + if (ptr_) + column_common (m, type, column); + else + { + // If this is a non-inverse relationship, then make the column have + // a dual interface: that of an object pointer and of an id column. + // The latter allows the user to, for example, use the is_null() + // test in a natural way. For inverse relationships there is no + // column and so the column interface is not available. + // + column_common (m, type, column, "_column_type_"); + + if (decl_) + { + os << "typedef" << endl + << "odb::query_pointer<" << endl + << " odb::pointer_query_columns<" << endl + << " " << class_fq_name (c) << "," << endl + << " " << name << "_alias_ > >" << endl + << name << "_pointer_type_;" + << endl; + + os << "struct " << name << "_type_: " << + name << "_pointer_type_, " << name << "_column_type_" + << "{"; + + column_ctor (name + "_type_", name + "_column_type_"); + + os << "};"; + } + } + + if (decl_) + os << "static const " << name << "_type_ " << name << ";" + << endl; + } + } + // // Dynamic traversal support. // diff --git a/odb/relational/common.hxx b/odb/relational/common.hxx index d552a72..276868c 100644 --- a/odb/relational/common.hxx +++ b/odb/relational/common.hxx @@ -42,7 +42,7 @@ namespace relational protected: // For virtual inheritance only. Should not be actually called. // - member_base (); // {assert (false);} + member_base (); protected: string var_override_; @@ -51,6 +51,176 @@ namespace relational string key_prefix_; }; + // Template argument is the database SQL type (sql_type). + // + template + struct member_base_impl: virtual member_base + { + typedef member_base_impl base_impl; + + member_base_impl (base const& x): base (x) {} + + protected: + member_base_impl () {} + + public: + virtual T const& + member_sql_type (semantics::data_member&) = 0; + + virtual void + traverse (semantics::data_member&); + + struct member_info + { + semantics::data_member& m; // Member. + semantics::type& t; // Cvr-unqualified member C++ type, note + // that m.type () may not be the same as t. + semantics::class_* ptr; // Pointed-to object if m is an object + // pointer. In this case t is the id type + // while fq_type_ is the pointer fq-type. + semantics::type* wrapper; // Wrapper type if member is a composite or + // container wrapper, also cvr-unqualified. + // In this case t is the wrapped type. + bool cq; // True if the original (wrapper) type + // is const-qualified. + T const* st; // Member SQL type (only simple values). + string& var; // Member variable name with trailing '_'. + + // C++ type fq-name. + // + string + fq_type (bool unwrap = true) const + { + semantics::names* hint; + + if (wrapper != 0 && unwrap) + { + // Use the hint from the wrapper unless the wrapped type + // is qualified. + // + hint = wrapper->get ("wrapper-hint"); + utype (*context::wrapper (*wrapper), hint); + return t.fq_name (hint); + } + + // Use the original type from 'm' instead of 't' since the hint may + // be invalid for a different type. Plus, if a type is overriden, + // then the fq_type must be as well. + // + if (ptr != 0) + { + semantics::type& t (utype (*id_member (*ptr), hint)); + return t.fq_name (hint); + } + else if (fq_type_.empty ()) + { + semantics::type& t (utype (m, hint)); + return t.fq_name (hint); + } + else + return fq_type_; + } + + string + ptr_fq_type () const + { + assert (ptr != 0); + + if (fq_type_.empty ()) + { + // If type is overridden so should fq_type so it is safe to + // get the type from the member. + // + semantics::names* hint; + semantics::type& t (utype (m, hint)); + return t.fq_name (hint); + } + else + return fq_type_; + } + + string const& fq_type_; + + member_info (semantics::data_member& m_, + semantics::type& t_, + semantics::type* wrapper_, + bool cq_, + string& var_, + string const& fq_type) + : m (m_), + t (t_), + ptr (0), + wrapper (wrapper_), + cq (cq_), + st (0), + var (var_), + fq_type_ (fq_type) + { + } + }; + + bool + container (member_info& mi) + { + // This cannot be a container if we have a type override. + // + return type_override_ == 0 && context::container (mi.m); + } + + // The false return value indicates that no further callbacks + // should be called for this member. + // + virtual bool + pre (member_info&) + { + return true; + } + + virtual void + post (member_info&) + { + } + + virtual void + traverse_composite (member_info&) + { + } + + virtual void + traverse_container (member_info&) + { + } + + // Note that by default traverse_object_pointer() will traverse the + // pointed-to object id type. + // + virtual void + traverse_object_pointer (member_info&); + + virtual void + traverse_simple (member_info&) = 0; + }; + + // + // + struct member_database_type_id: virtual member_base + { + typedef member_database_type_id base; + + member_database_type_id (semantics::type* type = 0, + string const& fq_type = string (), + string const& key_prefix = string ()) + : member_base (type, fq_type, key_prefix) + { + } + + virtual string + database_type_id (semantics::data_member&) + { + assert (false); + } + }; + // // struct query_columns_base: object_columns_base, virtual context @@ -66,8 +236,8 @@ namespace relational virtual void traverse_composite (semantics::data_member*, semantics::class_&); - virtual bool - traverse_column (semantics::data_member&, string const&, bool); + virtual void + traverse_pointer (semantics::data_member&, semantics::class_&); protected: bool decl_; @@ -103,13 +273,24 @@ namespace relational virtual void traverse_composite (semantics::data_member*, semantics::class_&); + virtual void + column_common (semantics::data_member&, + string const& type, + string const& column, + string const& suffix = "_type_"); + virtual bool traverse_column (semantics::data_member&, string const&, bool); + virtual void + traverse_pointer (semantics::data_member&, semantics::class_&); + protected: bool ptr_; bool decl_; + bool in_ptr_; // True if we are "inside" an object pointer. + string scope_; string table_; string default_table_; @@ -349,4 +530,6 @@ namespace relational } } +#include + #endif // ODB_RELATIONAL_COMMON_HXX diff --git a/odb/relational/common.txx b/odb/relational/common.txx new file mode 100644 index 0000000..1d3ecbf --- /dev/null +++ b/odb/relational/common.txx @@ -0,0 +1,112 @@ +// file : odb/relational/common.txx +// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +namespace relational +{ + // + // member_base_impl + // + + template + void member_base_impl:: + traverse (semantics::data_member& m) + { + if (transient (m)) + return; + + string var; + + if (!var_override_.empty ()) + var = var_override_; + else + { + string const& name (m.name ()); + var = name + (name[name.size () - 1] == '_' ? "" : "_"); + } + + bool cq (type_override_ != 0 ? false : const_type (m.type ())); + semantics::type& t (type_override_ != 0 ? *type_override_ : utype (m)); + + semantics::type* cont; + if (semantics::class_* c = object_pointer (t)) + { + semantics::type& t (utype (*id_member (*c))); + semantics::class_* comp (composite_wrapper (t)); + + member_info mi (m, + (comp != 0 ? *comp : t), + (comp != 0 && wrapper (t) ? &t : 0), + cq, + var, + fq_type_override_); // Pointer type. + + mi.ptr = c; + + if (comp == 0) + mi.st = &member_sql_type (m); + + if (pre (mi)) + { + traverse_object_pointer (mi); + post (mi); + } + } + else if (semantics::class_* c = composite_wrapper (t)) + { + // If t is a wrapper, pass the wrapped type. Also pass the + // original, wrapper type. + // + member_info mi (m, + *c, + (wrapper (t) ? &t : 0), + cq, + var, + fq_type_override_); + if (pre (mi)) + { + traverse_composite (mi); + post (mi); + } + } + // This cannot be a container if we have a type override. + // + else if (type_override_ == 0 && (cont = context::container (m))) + { + // The same unwrapping logic as for composite values. + // + member_info mi (m, + *cont, + (wrapper (t) ? &t : 0), + cq, + var, + fq_type_override_); + if (pre (mi)) + { + traverse_container (mi); + post (mi); + } + } + else + { + member_info mi (m, t, 0, cq, var, fq_type_override_); + mi.st = &member_sql_type (m); + + if (pre (mi)) + { + traverse_simple (mi); + post (mi); + } + } + } + + template + void member_base_impl:: + traverse_object_pointer (member_info& mi) + { + if (composite (mi.t)) // Already unwrapped. + traverse_composite (mi); + else + traverse_simple (mi); + } +} diff --git a/odb/relational/header.hxx b/odb/relational/header.hxx index cf103b5..dda9e2a 100644 --- a/odb/relational/header.hxx +++ b/odb/relational/header.hxx @@ -441,8 +441,11 @@ namespace relational // size_t n; - if (class_* kc = composite_wrapper (*kt)) - n = column_count (*kc).total; + class_* ptr (object_pointer (*kt)); + semantics::type& t (ptr == 0 ? *kt : utype (*id_member (*ptr))); + + if (class_* comp = composite_wrapper (t)) + n = column_count (*comp).total; else n = 1; @@ -461,19 +464,27 @@ namespace relational // // Value is also a key. // - //if (class_* vc = composite_wrapper (vt)) - // cond_columns += column_count (*vc).total; - //else - // cond_columns++; - + // class_* ptr (object_pointer (vt)); + // semantics::type& t (ptr == 0 ? vt : utype (*id_member (*ptr))); + // + // if (class_* comp = composite_wrapper (t)) + // cond_columns += column_count (*comp).total; + // else + // cond_columns++; + // break; } } - if (class_* vc = composite_wrapper (vt)) - data_columns += column_count (*vc).total; - else - data_columns++; + { + class_* ptr (object_pointer (vt)); + semantics::type& t (ptr == 0 ? vt : utype (*id_member (*ptr))); + + if (class_* comp = composite_wrapper (t)) + data_columns += column_count (*comp).total; + else + data_columns++; + } // Store column counts for the source generator. // @@ -786,7 +797,7 @@ namespace relational } } - os << "const data_image_type&, database&);" + os << "const data_image_type&, database*);" << endl; // insert_one @@ -1138,7 +1149,7 @@ namespace relational // init (object, image) // os << "static void" << endl - << "init (object_type&, const image_type&, database&);" + << "init (object_type&, const image_type&, database*);" << endl; // init (id_image, id) @@ -1503,7 +1514,7 @@ namespace relational // init (view, image) // os << "static void" << endl - << "init (view_type&, const image_type&, database&);" + << "init (view_type&, const image_type&, database*);" << endl; // column_count @@ -1605,7 +1616,7 @@ namespace relational // init (value, image) // os << "static void" << endl - << "init (value_type&, const image_type&, database&);" + << "init (value_type&, const image_type&, database*);" << endl; os << "};"; diff --git a/odb/relational/model.hxx b/odb/relational/model.hxx index 5f6470d..66db3d6 100644 --- a/odb/relational/model.hxx +++ b/odb/relational/model.hxx @@ -26,12 +26,10 @@ namespace relational { typedef object_columns base; - object_columns (sema_rel::model& model, - sema_rel::table& table, - string const& prefix = string ()) + object_columns (sema_rel::model& model, sema_rel::table& table) : model_ (model), table_ (table), - prefix_ (prefix), + pkey_ (0), id_override_ (false) { } @@ -77,20 +75,25 @@ namespace relational virtual void traverse (semantics::data_member& m, - semantics::class_& c, - std::string const& kp, - std::string const& dn) + semantics::type& t, + string const& kp, + string const& dn, + semantics::class_* to = 0) { // This overrides the member name for a composite container value // or key. // if (!kp.empty ()) { - id_prefix_ = kp + "."; - id_override_ = true; + semantics::class_* c (object_pointer (t)); + if (composite_wrapper (c == 0 ? t : utype (*id_member (*c)))) + { + id_prefix_ = kp + "."; + id_override_ = true; + } } - object_columns_base::traverse (m, c, kp, dn); + object_columns_base::traverse (m, t, kp, dn, to); } using object_columns_base::traverse; @@ -98,23 +101,22 @@ namespace relational virtual bool traverse_column (semantics::data_member& m, string const& name, bool) { - // Ignore inverse object pointers. - // - if (inverse (m)) - return false; + bool id (object_columns_base::id ()); // Id or part of. + bool null (!id && context::null (m, key_prefix_)); - string id (id_prefix_ + (prefix_.empty () ? m.name () : prefix_)); + string col_id (id_prefix_ + + (key_prefix_.empty () ? m.name () : key_prefix_)); sema_rel::column& c ( - model_.new_node ( - id, column_type (m, prefix_), context::null (m, prefix_))); + model_.new_node (col_id, column_type (), null)); + c.set ("cxx-node", static_cast (&m)); model_.new_edge (table_, c, name); // An id member cannot have a default value. // - if (!context::id (m)) + if (!id) { string const& d (default_ (m)); @@ -124,14 +126,12 @@ namespace relational // If we have options, add them. // - string const& o (column_options (m, prefix_)); + string const& o (column_options (m, key_prefix_)); if (!o.empty ()) c.options (o); - constraints (m, name, id, c); - reference (m, name, id, c); - + constraints (m, name, col_id, c); return true; } @@ -188,58 +188,129 @@ namespace relational string const& /* id */, sema_rel::column& c) { - if (!id (m)) - return; - - sema_rel::primary_key& pk ( - model_.new_node (m.count ("auto"))); - pk.set ("cxx-node", static_cast (&m)); - - model_.new_edge (pk, c); - - // In most databases the primary key constraint can be manipulated - // without an explicit name. So we use the special empty name for - // primary keys in order not to clash with columns and other - // constraints. If the target database does not support unnamed - // primary key manipulation, then the database-specific code will - // have to come up with a suitable name. - // - model_.new_edge (table_, pk, ""); + if (table_.is_a ()) + { + if (semantics::data_member* idm = id ()) + { + if (pkey_ == 0) + { + pkey_ = &model_.new_node ( + m.count ("auto")); + pkey_->set ("cxx-node", static_cast (idm)); + + // In most databases the primary key constraint can be + // manipulated without an explicit name. So we use the special + // empty name for primary keys in order not to clash with + // columns and other constraints. If the target database does + // not support unnamed primary key manipulation, then the + // database-specific code will have to come up with a suitable + // name. + // + model_.new_edge (table_, *pkey_, ""); + } + + model_.new_edge (*pkey_, c); + } + } } virtual void - reference (semantics::data_member& m, - string const& name, - string const& id, - sema_rel::column& c) + traverse_pointer (semantics::data_member& m, semantics::class_& c) { - semantics::class_* p (object_pointer (member_utype (m, prefix_))); + using sema_rel::column; - if (p == 0) + // Ignore inverse object pointers. + // + if (inverse (m, key_prefix_)) return; + string id (id_prefix_ + + (key_prefix_.empty () ? m.name () : key_prefix_)); + sema_rel::foreign_key& fk ( model_.new_node ( id, - table_name (*p), + table_name (c), true)); // deferred fk.set ("cxx-node", static_cast (&m)); - fk.referenced_columns ().push_back (column_name (*id_member (*p))); + bool simple; + + // Get referenced columns. + // + { + semantics::data_member& idm (*id_member (c)); + + instance ocl; + ocl->traverse (idm); + + for (object_columns_list::iterator i (ocl->begin ()); + i != ocl->end (); ++i) + fk.referenced_columns ().push_back (i->name); + + simple = (fk.referenced_columns ().size () == 1); + } + + // Get the position of the last column. + // + sema_rel::table::names_iterator i (table_.names_end ()); + + while (i != table_.names_begin ()) + { + --i; + + if (i->nameable ().is_a ()) + break; + } + + // Traverse the object pointer as columns. + // + object_columns_base::traverse_pointer (m, c); - model_.new_edge (fk, c); + // Add the newly added columns to the foreign key. + // + if (i != table_.names_end ()) + ++i; + else + i = table_.names_begin (); + + for (; i != table_.names_end (); ++i) + { + if (column* c = dynamic_cast (&i->nameable ())) + model_.new_edge (fk, *c); + else + break; + } // Derive the constraint name. Generally, we want it to be based - // on the column name. This is straightforward for single column - // references. In case of the composite ids, we will need to use - // the column prefix which is based on the data member name, - // unless overridden (see how the column pragma works for members - // of composite value types). @@ This is a TODO. Perhaps use the - // up-to-and-including composite member prefix? Though it can be - // empty. + // 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. // - model_.new_edge (table_, fk, name + "_fk"); + string name; + + if (simple) + name = fk.contains_begin ()->column ().name (); + else + { + string p (column_prefix (m, key_prefix_, default_name_)); + + if (p.empty ()) + p = public_name_db (m); + + name = column_prefix_ + p; + } + + // Add an underscore unless we already have one. + // + if (name[name.size () - 1] != '_') + name += '_'; + + model_.new_edge (table_, fk, name + "fk"); } protected: @@ -249,7 +320,7 @@ namespace relational protected: sema_rel::model& model_; sema_rel::table& table_; - string prefix_; + sema_rel::primary_key* pkey_; string id_prefix_; bool id_override_; }; @@ -310,13 +381,15 @@ namespace relational using semantics::type; using semantics::data_member; + using sema_rel::index; + using sema_rel::column; + // Ignore inverse containers of object pointers. // if (inverse (m, "value")) return; container_kind_type ck (container_kind (ct)); - type& vt (container_vt (ct)); qname const& name (table_name (m, table_prefix_)); @@ -332,15 +405,14 @@ namespace relational model_.new_edge (model_, t, name); - // object_id (simple value, for now) + // object_id // - string id_name (column_name (m, "id", "object_id")); { - instance oc (model_, t, "id"); - oc->traverse_column (m, id_name, true); + instance oc (model_, t); + oc->traverse (m, container_idt (m), "id", "object_id"); } - // Foreign key for the object id. + // Foreign key and index for the object id. // { sema_rel::foreign_key& fk ( @@ -349,105 +421,91 @@ namespace relational table_name (*context::top_object), false, // immediate sema_rel::foreign_key::cascade)); - fk.set ("cxx-node", static_cast (&m)); - fk.referenced_columns ().push_back ( - column_name ( - *id_member (*context::top_object))); + index& in (model_.new_node (id + ".id")); + in.set ("cxx-node", static_cast (&m)); + + // Get referenced columns. + // + { + data_member& idm (*id_member (*context::top_object)); + + instance ocl; + ocl->traverse (idm); + + for (object_columns_list::iterator i (ocl->begin ()); + i != ocl->end (); ++i) + fk.referenced_columns ().push_back (i->name); + } // All the columns we have in this table so far are for the - // object id. + // object id. Add them to the foreign key and the index. // for (sema_rel::table::names_iterator i (t.names_begin ()); i != t.names_end (); ++i) - model_.new_edge ( - fk, dynamic_cast (i->nameable ())); + { + column& c (dynamic_cast (i->nameable ())); - // Derive the constraint name. See the comment for the other - // foreign key code above. + model_.new_edge (fk, c); + model_.new_edge (in, c); + } + + // Derive the names. See the comment for the other foreign key + // code above. // - model_.new_edge (t, fk, id_name + "_fk"); - } + // Note also that id_name can be a column prefix (if id is + // composite), in which case it can be empty and if not, then + // it will most likely already contain a trailing underscore. + // + string id_name (column_name (m, "id", "object_id")); - // index (simple value) - // - string index_name; - bool ordered (ck == ck_ordered && !unordered (m)); - if (ordered) - { - instance oc (model_, t, "index"); - index_name = column_name (m, "index", "index"); - oc->traverse_column (m, index_name, true); - } + if (id_name.empty ()) + id_name = "object_id"; - // key (simple or composite value) - // - if (ck == ck_map || ck == ck_multimap) - { - type& kt (container_kt (ct)); + if (id_name[id_name.size () - 1] != '_') + id_name += '_'; - if (semantics::class_* ckt = composite_wrapper (kt)) - { - instance oc (model_, t); - oc->traverse (m, *ckt, "key", "key"); - } - else - { - instance oc (model_, t, "key"); - string const& name (column_name (m, "key", "key")); - oc->traverse_column (m, name, true); - } + model_.new_edge (t, fk, id_name + "fk"); + + model_.new_edge ( + model_, in, name + "_" + id_name + "i"); } - // value (simple or composite value) + // index (simple value) // + bool ordered (ck == ck_ordered && !unordered (m)); + if (ordered) { - if (semantics::class_* cvt = composite_wrapper (vt)) - { - instance oc (model_, t); - oc->traverse (m, *cvt, "value", "value"); - } - else - { - instance oc (model_, t, "value"); - string const& name (column_name (m, "value", "value")); - oc->traverse_column (m, name, true); - } - } + instance oc (model_, t); + oc->traverse (m, container_it (ct), "index", "index"); - // Create indexes. - // - using sema_rel::index; - using sema_rel::column; + string col_name (column_name (m, "index", "index")); - { - index& i (model_.new_node (id + ".id")); - i.set ("cxx-node", static_cast (&m)); + index& in (model_.new_node (id + ".index")); + in.set ("cxx-node", static_cast (&m)); model_.new_edge ( - i, dynamic_cast (t.find (id_name)->nameable ())); + in, dynamic_cast (t.find (col_name)->nameable ())); - //@@ Once id can be composite, we need to revise this (see - // a comment for the foreign key generation above). - // model_.new_edge ( - model_, i, name + "_" + id_name + "_i"); + model_, in, name + "_" + col_name + "_i"); } - if (ordered) + // key + // + if (ck == ck_map || ck == ck_multimap) { - index& i (model_.new_node (id + ".index")); - i.set ("cxx-node", static_cast (&m)); - - model_.new_edge ( - i, dynamic_cast (t.find (index_name)->nameable ())); + instance oc (model_, t); + oc->traverse (m, container_kt (ct), "key", "key"); + } - // This is always a single column (simple value). - // - model_.new_edge ( - model_, i, name + "_" + index_name + "_i"); + // value + // + { + instance oc (model_, t); + oc->traverse (m, container_vt (ct), "value", "value"); } } diff --git a/odb/relational/mssql/common.cxx b/odb/relational/mssql/common.cxx index f4ce42e..20be733 100644 --- a/odb/relational/mssql/common.cxx +++ b/odb/relational/mssql/common.cxx @@ -16,91 +16,10 @@ namespace relational // member_base // - void member_base:: - traverse (semantics::data_member& m) + sql_type const& member_base:: + member_sql_type (semantics::data_member& m) { - if (transient (m)) - return; - - string var; - - if (!var_override_.empty ()) - var = var_override_; - else - { - string const& name (m.name ()); - var = name + (name[name.size () - 1] == '_' ? "" : "_"); - } - - bool cq (type_override_ != 0 ? false : const_type (m.type ())); - semantics::type& t (type_override_ != 0 ? *type_override_ : utype (m)); - - semantics::type* cont; - if (semantics::class_* c = composite_wrapper (t)) - { - // If t is a wrapper, pass the wrapped type. Also pass the - // original, wrapper type. - // - member_info mi (m, - *c, - (wrapper (t) ? &t : 0), - cq, - var, - fq_type_override_); - if (pre (mi)) - { - traverse_composite (mi); - post (mi); - } - } - // This cannot be a container if we have a type override. - // - else if (type_override_ == 0 && (cont = context::container (m))) - { - // The same unwrapping logic as for composite values. - // - member_info mi (m, - *cont, - (wrapper (t) ? &t : 0), - cq, - var, - fq_type_override_); - if (pre (mi)) - { - traverse_container (mi); - post (mi); - } - } - else - { - sql_type const& st (column_sql_type (m, key_prefix_)); - - if (semantics::class_* c = object_pointer (t)) - { - member_info mi (m, - utype (*id_member (*c)), - 0, - cq, - var, - fq_type_override_); - mi.st = &st; - if (pre (mi)) - { - traverse_object_pointer (mi); - post (mi); - } - } - else - { - member_info mi (m, t, 0, cq, var, fq_type_override_); - mi.st = &st; - if (pre (mi)) - { - traverse_simple (mi); - post (mi); - } - } - } + return parse_sql_type (column_type (m, key_prefix_), m); } void member_base:: @@ -406,15 +325,23 @@ namespace relational }; member_database_type_id:: + member_database_type_id (base const& x) + : member_base::base (x), // virtual base + base (x) + { + } + + member_database_type_id:: member_database_type_id (semantics::type* type, string const& fq_type, string const& key_prefix) - : relational::member_base (type, fq_type, key_prefix) + : member_base::base (type, fq_type, key_prefix), // virtual base + base (type, fq_type, key_prefix) { } string member_database_type_id:: - database_type_id (type& m) + database_type_id (semantics::data_member& m) { type_id_.clear (); member_base::traverse (m); @@ -535,6 +462,8 @@ namespace relational type_id_ = "mssql::id_rowversion"; } + entry member_database_type_id_; + // // query_columns // @@ -566,7 +495,7 @@ namespace relational { // For some types we need to pass precision and scale. // - sql_type const& st (column_sql_type (m)); + sql_type const& st (parse_sql_type (column_type (), m)); switch (st.type) { diff --git a/odb/relational/mssql/common.hxx b/odb/relational/mssql/common.hxx index 7f2009a..a04c9a3 100644 --- a/odb/relational/mssql/common.hxx +++ b/odb/relational/mssql/common.hxx @@ -12,118 +12,18 @@ namespace relational { namespace mssql { - struct member_base: virtual relational::member_base, context + struct member_base: virtual relational::member_base_impl, context { - member_base (base const& x): base (x) {} + member_base (base const& x): base (x), base_impl (x) {} // This c-tor is for the direct use inside the mssql namespace. // If you do use this c-tor, you should also explicitly call - // relational::member_base. + // relational::member_base (aka base). // member_base () {} - virtual void - traverse (semantics::data_member& m); - - struct member_info - { - semantics::data_member& m; // Member. - semantics::type& t; // Cvr-unqualified member C++ type, note - // that m.type () may not be the same as t. - semantics::type* wrapper; // Wrapper type if member is a composite or - // container wrapper, also cvr-unqualified. - // In this case t is the wrapped type. - bool cq; // True if the original (wrapper) type - // is const-qualified. - sql_type const* st; // Member SQL type (only simple values). - string& var; // Member variable name with trailing '_'. - - // C++ type fq-name. - // - string - fq_type (bool unwrap = true) const - { - semantics::names* hint; - - if (wrapper != 0 && unwrap) - { - // Use the hint from the wrapper unless the wrapped type - // is qualified. - // - hint = wrapper->get ("wrapper-hint"); - utype (*context::wrapper (*wrapper), hint); - return t.fq_name (hint); - } - - // Use the original type from 'm' instead of 't' since the hint - // may be invalid for a different type. Plus, if a type is - // overriden, then the fq_type must be as well. - // - if (fq_type_.empty ()) - { - semantics::type& t (utype (m, hint)); - return t.fq_name (hint); - } - else - return fq_type_; - } - - string const& fq_type_; - - member_info (semantics::data_member& m_, - semantics::type& t_, - semantics::type* wrapper_, - bool cq_, - string& var_, - string const& fq_type) - : m (m_), - t (t_), - wrapper (wrapper_), - cq (cq_), - st (0), - var (var_), - fq_type_ (fq_type) - { - } - }; - - bool - container (member_info& mi) - { - // This cannot be a container if we have a type override. - // - return type_override_ == 0 && context::container (mi.m); - } - - // The false return value indicates that no further callbacks - // should be called for this member. - // - virtual bool - pre (member_info&) - { - return true; - } - - virtual void - post (member_info&) - { - } - - virtual void - traverse_composite (member_info&) - { - } - - virtual void - traverse_container (member_info&) - { - } - - virtual void - traverse_object_pointer (member_info& mi) - { - traverse_simple (mi); - } + virtual sql_type const& + member_sql_type (semantics::data_member&); virtual void traverse_simple (member_info&); @@ -288,13 +188,17 @@ namespace relational string type_; }; - struct member_database_type_id: member_base + struct member_database_type_id: relational::member_database_type_id, + member_base { + member_database_type_id (base const&); + member_database_type_id (semantics::type* type = 0, string const& fq_type = string (), string const& key_prefix = string ()); - string - database_type_id (type&); + + virtual string + database_type_id (semantics::data_member&); virtual void traverse_composite (member_info&); @@ -361,13 +265,17 @@ namespace relational { has_long_data (bool& r): r_ (r) {} + virtual void + traverse_pointer (semantics::data_member& m, semantics::class_& c) + { + if (!inverse (m, key_prefix_)) + object_columns_base::traverse_pointer (m, c); + } + virtual bool traverse_column (semantics::data_member& m, string const&, bool) { - if (inverse (m)) - return false; - - sql_type const& st (column_sql_type (m)); + sql_type const& st (parse_sql_type (column_type (), m)); switch (st.type) { diff --git a/odb/relational/mssql/context.cxx b/odb/relational/mssql/context.cxx index e882ba3..312cc03 100644 --- a/odb/relational/mssql/context.cxx +++ b/odb/relational/mssql/context.cxx @@ -557,17 +557,20 @@ namespace relational } sql_type const& context:: - column_sql_type (semantics::data_member& m, string const& kp) + parse_sql_type (string const& t, semantics::data_member& m) { - string key (kp.empty () - ? string ("mssql-column-sql-type") - : "mssql-" + kp + "-column-sql-type"); + // If this proves to be too expensive, we can maintain a + // cache of parsed types. + // + data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t)); - if (!m.count (key)) + if (i != data_->sql_type_cache_.end ()) + return i->second; + else { try { - m.set (key, parse_sql_type (column_type (m, kp))); + return (data_->sql_type_cache_[t] = parse_sql_type (t)); } catch (invalid_sql_type const& e) { @@ -577,8 +580,6 @@ namespace relational throw operation_failed (); } } - - return m.get (key); } sql_type context:: diff --git a/odb/relational/mssql/context.hxx b/odb/relational/mssql/context.hxx index 7530a9f..fe30746 100644 --- a/odb/relational/mssql/context.hxx +++ b/odb/relational/mssql/context.hxx @@ -5,6 +5,8 @@ #ifndef ODB_RELATIONAL_MSSQL_CONTEXT_HXX #define ODB_RELATIONAL_MSSQL_CONTEXT_HXX +#include + #include namespace relational @@ -83,8 +85,7 @@ namespace relational { public: sql_type const& - column_sql_type (semantics::data_member&, - string const& key_prefix = string ()); + parse_sql_type (string const&, semantics::data_member&); public: struct invalid_sql_type @@ -132,6 +133,9 @@ namespace relational struct data: base_context::data { data (std::ostream& os): base_context::data (os) {} + + typedef std::map sql_type_cache; + sql_type_cache sql_type_cache_; }; data* data_; }; diff --git a/odb/relational/mssql/model.cxx b/odb/relational/mssql/model.cxx index aec027c..c3f4948 100644 --- a/odb/relational/mssql/model.cxx +++ b/odb/relational/mssql/model.cxx @@ -28,7 +28,7 @@ namespace relational { // Make sure the column is mapped to an integer or DECIMAL type. // - switch (column_sql_type (m).type) + switch (parse_sql_type (column_type (), m).type) { case sql_type::BIT: case sql_type::TINYINT: diff --git a/odb/relational/mssql/source.cxx b/odb/relational/mssql/source.cxx index 1c11984..1b800aa 100644 --- a/odb/relational/mssql/source.cxx +++ b/odb/relational/mssql/source.cxx @@ -41,17 +41,16 @@ namespace relational virtual void column (semantics::data_member& m, - string const& key_prefix, string const& table, string const& column) { // Don't add a column for auto id in the INSERT statement. // if (!(sk_ == statement_insert && - key_prefix.empty () && - id (m) && auto_(m))) + key_prefix_.empty () && + context::id (m) && auto_(m))) // Only simple id can be auto. { - base::column (m, key_prefix, table, column); + base::column (m, table, column); } } }; @@ -413,181 +412,21 @@ namespace relational // init image // - struct init_image_member: relational::init_image_member, member_base + struct init_image_member: relational::init_image_member_impl, + member_base { init_image_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_database_type_id_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) { - // Ignore containers (they get their own table) and inverse - // object pointers (they are not present in this binding). - // - if (container (mi) || inverse (mi.m, key_prefix_)) - return false; - - if (!member_override_.empty ()) - member = member_override_; - else - { - // If we are generating standard init() and this member - // contains version, ignore it. - // - if (version (mi.m)) - return false; - - // For SQL Server we don't send auto id in INSERT statement - // (nor in UPDATE, as for other databases). So ignore it - // altogether. - // - if (id (mi.m) && auto_ (mi.m)) - return false; - - string const& name (mi.m.name ()); - member = "o." + name; - - os << "// " << name << endl - << "//" << endl; - - // If the whole class is readonly, then we will never be - // called with sk == statement_update. - // - 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_insert)"; - } - } - - // If this is a wrapped composite value, then we need to - // "unwrap" it. For simple values this is taken care of - // by the value_traits specializations. - // - if (mi.wrapper != 0 && composite (mi.t)) - { - // Here we need the wrapper type, not the wrapped type. - // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "get_ref (" + member + ")"; - } - - if (composite (mi.t)) - { - os << "{"; - traits = "composite_value_traits< " + mi.fq_type () + " >"; - } - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (semantics::class_* c = object_pointer (mt)) - { - type = "obj_traits::id_type"; - db_type_id = member_database_type_id_.database_type_id (mi.m); - - // Handle NULL pointers and extract the id. - // - os << "{" - << "typedef object_traits< " << class_fq_name (*c) << - " > obj_traits;"; - - if (weak_pointer (mt)) - { - os << "typedef pointer_traits< " << mi.fq_type () << - " > wptr_traits;" - << "typedef pointer_traits< wptr_traits::" << - "strong_pointer_type > ptr_traits;" - << endl - << "wptr_traits::strong_pointer_type sp (" << - "wptr_traits::lock (" << member << "));"; - - member = "sp"; - } - else - os << "typedef pointer_traits< " << mi.fq_type () << - " > ptr_traits;" - << endl; - - os << "bool is_null (ptr_traits::null_ptr (" << member << "));" - << "if (!is_null)" - << "{" - << "const " << type << "& id (" << endl; - - if (lazy_pointer (mt)) - os << "ptr_traits::object_id< ptr_traits::element_type > (" << - member << ")"; - else - os << "obj_traits::id (ptr_traits::get_ref (" << member << "))"; - - os << ");" - << endl; - - member = "id"; - } - else - { - type = mi.fq_type (); - db_type_id = member_database_type_id_.database_type_id (mi.m); - - os << "{" - << "bool is_null;"; - } - - traits = "mssql::value_traits<\n " - + type + ",\n " - + db_type_id + " >"; - } - - return true; } virtual void - post (member_info& mi) + set_null (member_info& mi) { - if (composite (mi.t)) - os << "}"; - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - if (object_pointer (member_utype (mi.m, key_prefix_))) - { - os << "}" - << "else" << endl; - - if (!null (mi.m, key_prefix_)) - os << "throw null_pointer ();"; - else - os << "i." << mi.var << "size_ind = SQL_NULL_DATA;"; - } - - os << "}"; - } - } - - virtual void - traverse_composite (member_info& mi) - { - os << traits << "::init (" << endl - << "i." << mi.var << "value," << endl - << member << "," << endl - << "sk);"; + os << "i." << mi.var << "size_ind = SQL_NULL_DATA;"; } virtual void @@ -800,14 +639,6 @@ namespace relational << "i." << mi.var << "value, is_null, " << member << ");" << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 8;"; } - - private: - string type; - string db_type_id; - string member; - string traits; - - member_database_type_id member_database_type_id_; }; entry init_image_member_; @@ -815,147 +646,21 @@ namespace relational // init value // - struct init_value_member: relational::init_value_member, member_base + struct init_value_member: relational::init_value_member_impl, + member_base { init_value_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_database_type_id_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - if (!member_override_.empty ()) - member = member_override_; - else - { - string const& name (mi.m.name ()); - member = "o." + name; - - if (mi.cq) - member = "const_cast< " + mi.fq_type (false) + "& > (" + - member + ")"; - - os << "// " << name << endl - << "//" << endl; - } - - // If this is a wrapped composite value, then we need to - // "unwrap" it. For simple values this is taken care of - // by the value_traits specializations. - // - if (mi.wrapper != 0 && composite (mi.t)) - { - // Here we need the wrapper type, not the wrapped type. - // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "set_ref (\n" + member + ")"; - } - - if (composite (mi.t)) - traits = "composite_value_traits< " + mi.fq_type () + " >"; - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (semantics::class_* c = object_pointer (mt)) - { - type = "obj_traits::id_type"; - db_type_id = member_database_type_id_.database_type_id (mi.m); - - // Handle NULL pointers and extract the id. - // - os << "{" - << "typedef object_traits< " << class_fq_name (*c) << - " > obj_traits;" - << "typedef pointer_traits< " << mi.fq_type () << - " > ptr_traits;" - << endl - << "if (i." << mi.var << "size_ind == SQL_NULL_DATA)" << endl; - - if (null (mi.m, key_prefix_)) - os << member << " = ptr_traits::pointer_type ();"; - else - os << "throw null_pointer ();"; - - os << "else" - << "{" - << type << " id;"; - - member = "id"; - } - else - { - type = mi.fq_type (); - db_type_id = member_database_type_id_.database_type_id (mi.m); - } - - traits = "mssql::value_traits<\n " - + type + ",\n " - + db_type_id + " >"; - } - - return true; - } - - virtual void - post (member_info& mi) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) { - if (composite (mi.t)) - return; - - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (object_pointer (mt)) - { - if (!member_override_.empty ()) - member = member_override_; - else - { - member = "o." + mi.m.name (); - - if (mi.cq) - member = "const_cast< " + mi.fq_type (false) + "& > (" + - member + ")"; - } - - if (lazy_pointer (mt)) - os << member << " = ptr_traits::pointer_type (db, id);"; - else - os << "// If a compiler error points to the line below, then" << endl - << "// it most likely means that a pointer used in a member" << endl - << "// cannot be initialized from an object pointer." << endl - << "//" << endl - << member << " = ptr_traits::pointer_type (" << endl - << "db.load< obj_traits::object_type > (id));"; - - os << "}" - << "}"; - } } virtual void - traverse_composite (member_info& mi) + get_null (member_info& mi) { - os << traits << "::init (" << endl - << member << "," << endl - << "i." << mi.var << "value," << endl - << "db);" - << endl; + os << "i." << mi.var << "size_ind == SQL_NULL_DATA"; } virtual void @@ -1141,14 +846,6 @@ namespace relational << "i." << mi.var << "size_ind == SQL_NULL_DATA);" << endl; } - - private: - string type; - string db_type_id; - string traits; - string member; - - member_database_type_id member_database_type_id_; }; entry init_value_member_; @@ -1172,7 +869,7 @@ namespace relational size_t n (cols.size ()); for (statement_columns::iterator i (cols.begin ()); n != 0; --n) { - sql_type const& st (column_sql_type (*i->member, i->key_prefix)); + sql_type const& st (parse_sql_type (i->type, *i->member)); bool l (false); diff --git a/odb/relational/mysql/common.cxx b/odb/relational/mysql/common.cxx index c8bad12..b8fa4c3 100644 --- a/odb/relational/mysql/common.cxx +++ b/odb/relational/mysql/common.cxx @@ -16,91 +16,10 @@ namespace relational // member_base // - void member_base:: - traverse (semantics::data_member& m) + sql_type const& member_base:: + member_sql_type (semantics::data_member& m) { - if (transient (m)) - return; - - string var; - - if (!var_override_.empty ()) - var = var_override_; - else - { - string const& name (m.name ()); - var = name + (name[name.size () - 1] == '_' ? "" : "_"); - } - - bool cq (type_override_ != 0 ? false : const_type (m.type ())); - semantics::type& t (type_override_ != 0 ? *type_override_ : utype (m)); - - semantics::type* cont; - if (semantics::class_* c = composite_wrapper (t)) - { - // If t is a wrapper, pass the wrapped type. Also pass the - // original, wrapper type. - // - member_info mi (m, - *c, - (wrapper (t) ? &t : 0), - cq, - var, - fq_type_override_); - if (pre (mi)) - { - traverse_composite (mi); - post (mi); - } - } - // This cannot be a container if we have a type override. - // - else if (type_override_ == 0 && (cont = context::container (m))) - { - // The same unwrapping logic as for composite values. - // - member_info mi (m, - *cont, - (wrapper (t) ? &t : 0), - cq, - var, - fq_type_override_); - if (pre (mi)) - { - traverse_container (mi); - post (mi); - } - } - else - { - sql_type const& st (column_sql_type (m, key_prefix_)); - - if (semantics::class_* c = object_pointer (t)) - { - member_info mi (m, - utype (*id_member (*c)), - 0, - cq, - var, - fq_type_override_); - mi.st = &st; - if (pre (mi)) - { - traverse_object_pointer (mi); - post (mi); - } - } - else - { - member_info mi (m, t, 0, cq, var, fq_type_override_); - mi.st = &st; - if (pre (mi)) - { - traverse_simple (mi); - post (mi); - } - } - } + return parse_sql_type (column_type (m, key_prefix_), m); } void member_base:: @@ -363,10 +282,18 @@ namespace relational }; member_database_type_id:: + member_database_type_id (base const& x) + : member_base::base (x), // virtual base + base (x) + { + } + + member_database_type_id:: member_database_type_id (semantics::type* type, - string const& fq_type, - string const& key_prefix) - : relational::member_base (type, fq_type, key_prefix) + string const& fq_type, + string const& key_prefix) + : member_base::base (type, fq_type, key_prefix), // virtual base + base (type, fq_type, key_prefix) { } @@ -437,6 +364,8 @@ namespace relational type_id_ = "mysql::id_set"; } + entry member_database_type_id_; + // // query_columns // diff --git a/odb/relational/mysql/common.hxx b/odb/relational/mysql/common.hxx index c49bd09..93f5fbd 100644 --- a/odb/relational/mysql/common.hxx +++ b/odb/relational/mysql/common.hxx @@ -12,118 +12,18 @@ namespace relational { namespace mysql { - struct member_base: virtual relational::member_base, context + struct member_base: virtual relational::member_base_impl, context { - member_base (base const& x): base (x) {} + member_base (base const& x): base (x), base_impl (x) {} // This c-tor is for the direct use inside the mysql namespace. // If you do use this c-tor, you should also explicitly call - // relational::member_base. + // relational::member_base (aka base). // member_base () {} - virtual void - traverse (semantics::data_member& m); - - struct member_info - { - semantics::data_member& m; // Member. - semantics::type& t; // Cvr-unqualified member C++ type, note - // that m.type () may not be the same as t. - semantics::type* wrapper; // Wrapper type if member is a composite or - // container wrapper, also cvr-unqualified. - // In this case t is the wrapped type. - bool cq; // True if the original (wrapper) type - // is const-qualified. - sql_type const* st; // Member SQL type (only simple values). - string& var; // Member variable name with trailing '_'. - - // C++ type fq-name. - // - string - fq_type (bool unwrap = true) const - { - semantics::names* hint; - - if (wrapper != 0 && unwrap) - { - // Use the hint from the wrapper unless the wrapped type - // is qualified. - // - hint = wrapper->get ("wrapper-hint"); - utype (*context::wrapper (*wrapper), hint); - return t.fq_name (hint); - } - - // Use the original type from 'm' instead of 't' since the hint - // may be invalid for a different type. Plus, if a type is - // overriden, then the fq_type must be as well. - // - if (fq_type_.empty ()) - { - semantics::type& t (utype (m, hint)); - return t.fq_name (hint); - } - else - return fq_type_; - } - - string const& fq_type_; - - member_info (semantics::data_member& m_, - semantics::type& t_, - semantics::type* wrapper_, - bool cq_, - string& var_, - string const& fq_type) - : m (m_), - t (t_), - wrapper (wrapper_), - cq (cq_), - st (0), - var (var_), - fq_type_ (fq_type) - { - } - }; - - bool - container (member_info& mi) - { - // This cannot be a container if we have a type override. - // - return type_override_ == 0 && context::container (mi.m); - } - - // The false return value indicates that no further callbacks - // should be called for this member. - // - virtual bool - pre (member_info&) - { - return true; - } - - virtual void - post (member_info&) - { - } - - virtual void - traverse_composite (member_info&) - { - } - - virtual void - traverse_container (member_info&) - { - } - - virtual void - traverse_object_pointer (member_info& mi) - { - traverse_simple (mi); - } + virtual sql_type const& + member_sql_type (semantics::data_member&); virtual void traverse_simple (member_info&); @@ -220,12 +120,16 @@ namespace relational string type_; }; - struct member_database_type_id: member_base + struct member_database_type_id: relational::member_database_type_id, + member_base { + member_database_type_id (base const&); + member_database_type_id (semantics::type* type = 0, string const& fq_type = string (), string const& key_prefix = string ()); - string + + virtual string database_type_id (type&); virtual void diff --git a/odb/relational/mysql/context.cxx b/odb/relational/mysql/context.cxx index b8327eb..ad7236c 100644 --- a/odb/relational/mysql/context.cxx +++ b/odb/relational/mysql/context.cxx @@ -316,17 +316,20 @@ namespace relational // sql_type const& context:: - column_sql_type (semantics::data_member& m, string const& kp) + parse_sql_type (string const& t, semantics::data_member& m) { - string key (kp.empty () - ? string ("mysql-column-sql-type") - : "mysql-" + kp + "-column-sql-type"); + // If this proves to be too expensive, we can maintain a + // cache of parsed types. + // + data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t)); - if (!m.count (key)) + if (i != data_->sql_type_cache_.end ()) + return i->second; + else { try { - m.set (key, parse_sql_type (column_type (m, kp))); + return (data_->sql_type_cache_[t] = parse_sql_type (t)); } catch (invalid_sql_type const& e) { @@ -336,8 +339,6 @@ namespace relational throw operation_failed (); } } - - return m.get (key); } sql_type context:: diff --git a/odb/relational/mysql/context.hxx b/odb/relational/mysql/context.hxx index 49b825e..294b5d1 100644 --- a/odb/relational/mysql/context.hxx +++ b/odb/relational/mysql/context.hxx @@ -5,6 +5,7 @@ #ifndef ODB_RELATIONAL_MYSQL_CONTEXT_HXX #define ODB_RELATIONAL_MYSQL_CONTEXT_HXX +#include #include #include @@ -80,8 +81,7 @@ namespace relational { public: sql_type const& - column_sql_type (semantics::data_member&, - string const& key_prefix = string ()); + parse_sql_type (string const&, semantics::data_member&); public: struct invalid_sql_type @@ -138,6 +138,9 @@ namespace relational struct data: base_context::data { data (std::ostream& os): base_context::data (os) {} + + typedef std::map sql_type_cache; + sql_type_cache sql_type_cache_; }; data* data_; }; diff --git a/odb/relational/mysql/model.cxx b/odb/relational/mysql/model.cxx index 8e07f38..11ca157 100644 --- a/odb/relational/mysql/model.cxx +++ b/odb/relational/mysql/model.cxx @@ -35,7 +35,7 @@ namespace relational { // Make sure the column is mapped to an ENUM or integer type. // - sql_type const& t (column_sql_type (m)); + sql_type const& t (parse_sql_type (column_type (), m)); switch (t.type) { diff --git a/odb/relational/mysql/source.cxx b/odb/relational/mysql/source.cxx index 91b3061..3a784af 100644 --- a/odb/relational/mysql/source.cxx +++ b/odb/relational/mysql/source.cxx @@ -68,7 +68,6 @@ namespace relational virtual void column (semantics::data_member& m, - string const& key_prefix, string const& table, string const& column) { @@ -110,10 +109,12 @@ namespace relational // to value_traits. // + string type (column_type ()); + if (sk_ != statement_select || - column_sql_type (m, key_prefix).type != sql_type::ENUM) + parse_sql_type (type, m).type != sql_type::ENUM) { - base::column (m, key_prefix, table, column); + base::column (m, table, column); return; } @@ -140,7 +141,8 @@ namespace relational r += ")"; - sc_.push_back (relational::statement_column (r, m, key_prefix)); + sc_.push_back ( + relational::statement_column (r, type, m, key_prefix_)); } }; entry object_columns_; @@ -154,7 +156,9 @@ namespace relational { // The same idea as in object_columns. // - if (column_sql_type (m).type != sql_type::ENUM) + string type (column_type ()); + + if (parse_sql_type (type, m).type != sql_type::ENUM) { base::column (m, column); return; @@ -168,7 +172,7 @@ namespace relational r += column; r += ")"; - sc_.push_back (relational::statement_column (r, m)); + sc_.push_back (relational::statement_column (r, type, m)); } }; entry view_columns_; @@ -568,188 +572,37 @@ namespace relational // init image // - struct init_image_member: relational::init_image_member, member_base + struct init_image_member: relational::init_image_member_impl, + member_base { init_image_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_database_type_id_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) + : 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) - { - // Ignore containers (they get their own table) and inverse - // object pointers (they are not present in this binding). - // - if (container (mi) || inverse (mi.m, key_prefix_)) - return false; - - if (!member_override_.empty ()) - member = member_override_; - else - { - // If we are generating standard init() and this member - // contains version, ignore it. - // - if (version (mi.m)) - return false; - - string const& name (mi.m.name ()); - member = "o." + name; - - os << "// " << name << endl - << "//" << endl; - - // If the whole class is readonly, then we will never be - // called with sk == statement_update. - // - 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_insert)"; - } - } - - // If this is a wrapped composite value, then we need to - // "unwrap" it. For simple values this is taken care of - // by the value_traits specializations. - // - if (mi.wrapper != 0 && composite (mi.t)) - { - // Here we need the wrapper type, not the wrapped type. - // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "get_ref (" + member + ")"; - } - - if (composite (mi.t)) - { - os << "{"; - traits = "composite_value_traits< " + mi.fq_type () + " >"; - } - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (semantics::class_* c = object_pointer (mt)) - { - type = "obj_traits::id_type"; - db_type_id = member_database_type_id_.database_type_id (mi.m); - - // Handle NULL pointers and extract the id. - // - os << "{" - << "typedef object_traits< " << class_fq_name (*c) << - " > obj_traits;"; - - if (weak_pointer (mt)) - { - os << "typedef pointer_traits< " << mi.fq_type () << - " > wptr_traits;" - << "typedef pointer_traits< wptr_traits::" << - "strong_pointer_type > ptr_traits;" - << endl - << "wptr_traits::strong_pointer_type sp (" << - "wptr_traits::lock (" << member << "));"; - - member = "sp"; - } - else - os << "typedef pointer_traits< " << mi.fq_type () << - " > ptr_traits;" - << endl; - - os << "bool is_null (ptr_traits::null_ptr (" << member << "));" - << "if (!is_null)" - << "{" - << "const " << type << "& id (" << endl; - - if (lazy_pointer (mt)) - os << "ptr_traits::object_id< ptr_traits::element_type > (" << - member << ")"; - else - os << "obj_traits::id (ptr_traits::get_ref (" << member << "))"; - - os << ");" - << endl; - - member = "id"; - } - else - { - type = mi.fq_type (); - db_type_id = member_database_type_id_.database_type_id (mi.m); - - os << "{" - << "bool is_null;"; - } - - traits = "mysql::value_traits<\n " - + type + ",\n " - + db_type_id + " >"; - } - - return true; - } - virtual void - post (member_info& mi) + set_null (member_info& mi) { - if (composite (mi.t)) - os << "}"; - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - if (object_pointer (member_utype (mi.m, key_prefix_))) - { - os << "}"; - - if (!null (mi.m, key_prefix_)) - os << "else" << endl - << "throw null_pointer ();"; - } - - os << "i." << mi.var << "null = is_null;" - << "}"; - } - } - - virtual void - traverse_composite (member_info& mi) - { - os << "if (" << traits << "::init (" << endl - << "i." << mi.var << "value," << endl - << member << "," << endl - << "sk))" << endl - << "grew = true;"; + os << "i." << mi.var << "null = 1;"; } virtual void traverse_integer (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "null = is_null;"; } virtual void traverse_float (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "null = is_null;"; } virtual void @@ -764,6 +617,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "null = is_null;" << "i." << mi.var << "size = static_cast (size);" << "grew = grew || (cap != i." << mi.var << "value.capacity ());"; } @@ -772,7 +626,8 @@ namespace relational traverse_date_time (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "null = is_null;"; } virtual void @@ -787,6 +642,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "null = is_null;" << "i." << mi.var << "size = static_cast (size);" << "grew = grew || (cap != i." << mi.var << "value.capacity ());"; } @@ -801,6 +657,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "null = is_null;" << "i." << mi.var << "size = static_cast (size);" << "grew = grew || (cap != i." << mi.var << "value.capacity ());"; } @@ -817,6 +674,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "null = is_null;" << "i." << mi.var << "size = static_cast (size);"; } @@ -830,7 +688,9 @@ namespace relational << "i." << mi.var << "size," << endl << "is_null," << endl << member << "))" << endl - << "grew = true;"; + << "grew = true;" + << endl + << "i." << mi.var << "null = is_null;"; } virtual void @@ -845,17 +705,10 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "null = is_null;" << "i." << mi.var << "size = static_cast (size);" << "grew = grew || (cap != i." << mi.var << "value.capacity ());"; } - - private: - string type; - string db_type_id; - string member; - string traits; - - member_database_type_id member_database_type_id_; }; entry init_image_member_; @@ -863,147 +716,21 @@ namespace relational // init value // - struct init_value_member: relational::init_value_member, member_base + struct init_value_member: relational::init_value_member_impl, + member_base { init_value_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_database_type_id_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - if (!member_override_.empty ()) - member = member_override_; - else - { - string const& name (mi.m.name ()); - member = "o." + name; - - if (mi.cq) - member = "const_cast< " + mi.fq_type (false) + "& > (" + - member + ")"; - - os << "// " << name << endl - << "//" << endl; - } - - // If this is a wrapped composite value, then we need to - // "unwrap" it. For simple values this is taken care of - // by the value_traits specializations. - // - if (mi.wrapper != 0 && composite (mi.t)) - { - // Here we need the wrapper type, not the wrapped type. - // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "set_ref (\n" + member + ")"; - } - - if (composite (mi.t)) - traits = "composite_value_traits< " + mi.fq_type () + " >"; - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (semantics::class_* c = object_pointer (mt)) - { - type = "obj_traits::id_type"; - db_type_id = member_database_type_id_.database_type_id (mi.m); - - // Handle NULL pointers and extract the id. - // - os << "{" - << "typedef object_traits< " << class_fq_name (*c) << - " > obj_traits;" - << "typedef pointer_traits< " << mi.fq_type () << - " > ptr_traits;" - << endl - << "if (i." << mi.var << "null)" << endl; - - if (null (mi.m, key_prefix_)) - os << member << " = ptr_traits::pointer_type ();"; - else - os << "throw null_pointer ();"; - - os << "else" - << "{" - << type << " id;"; - - member = "id"; - } - else - { - type = mi.fq_type (); - db_type_id = member_database_type_id_.database_type_id (mi.m); - } - - traits = "mysql::value_traits<\n " - + type + ",\n " - + db_type_id + " >"; - } - - return true; - } - - virtual void - post (member_info& mi) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) { - if (composite (mi.t)) - return; - - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (object_pointer (mt)) - { - if (!member_override_.empty ()) - member = member_override_; - else - { - member = "o." + mi.m.name (); - - if (mi.cq) - member = "const_cast< " + mi.fq_type (false) + "& > (" + - member + ")"; - } - - if (lazy_pointer (mt)) - os << member << " = ptr_traits::pointer_type (db, id);"; - else - os << "// If a compiler error points to the line below, then" << endl - << "// it most likely means that a pointer used in a member" << endl - << "// cannot be initialized from an object pointer." << endl - << "//" << endl - << member << " = ptr_traits::pointer_type (" << endl - << "db.load< obj_traits::object_type > (id));"; - - os << "}" - << "}"; - } } virtual void - traverse_composite (member_info& mi) + get_null (member_info& mi) { - os << traits << "::init (" << endl - << member << "," << endl - << "i." << mi.var << "value," << endl - << "db);" - << endl; + os << "i." << mi.var << "null"; } virtual void @@ -1107,14 +834,6 @@ namespace relational << "i." << mi.var << "null);" << endl; } - - private: - string type; - string db_type_id; - string traits; - string member; - - member_database_type_id member_database_type_id_; }; entry init_value_member_; diff --git a/odb/relational/oracle/common.cxx b/odb/relational/oracle/common.cxx index 88183cf..661bb4e 100644 --- a/odb/relational/oracle/common.cxx +++ b/odb/relational/oracle/common.cxx @@ -16,91 +16,10 @@ namespace relational // member_base // - void member_base:: - traverse (semantics::data_member& m) + sql_type const& member_base:: + member_sql_type (semantics::data_member& m) { - if (transient (m)) - return; - - string var; - - if (!var_override_.empty ()) - var = var_override_; - else - { - string const& name (m.name ()); - var = name + (name[name.size () - 1] == '_' ? "" : "_"); - } - - bool cq (type_override_ != 0 ? false : const_type (m.type ())); - semantics::type& t (type_override_ != 0 ? *type_override_ : utype (m)); - - semantics::type* cont; - if (semantics::class_* c = composite_wrapper (t)) - { - // If t is a wrapper, pass the wrapped type. Also pass the - // original, wrapper type. - // - member_info mi (m, - *c, - (wrapper (t) ? &t : 0), - cq, - var, - fq_type_override_); - if (pre (mi)) - { - traverse_composite (mi); - post (mi); - } - } - // This cannot be a container if we have a type override. - // - else if (type_override_ == 0 && (cont = context::container (m))) - { - // The same unwrapping logic as for composite values. - // - member_info mi (m, - *cont, - (wrapper (t) ? &t : 0), - cq, - var, - fq_type_override_); - if (pre (mi)) - { - traverse_container (mi); - post (mi); - } - } - else - { - sql_type const& st (column_sql_type (m, key_prefix_)); - - if (semantics::class_* c = object_pointer (t)) - { - member_info mi (m, - utype (*id_member (*c)), - 0, - cq, - var, - fq_type_override_); - mi.st = &st; - if (pre (mi)) - { - traverse_object_pointer (mi); - post (mi); - } - } - else - { - member_info mi (m, t, 0, cq, var, fq_type_override_); - mi.st = &st; - if (pre (mi)) - { - traverse_simple (mi); - post (mi); - } - } - } + return parse_sql_type (column_type (m, key_prefix_), m); } void member_base:: @@ -374,10 +293,18 @@ namespace relational } member_database_type_id:: + member_database_type_id (base const& x) + : member_base::base (x), // virtual base + base (x) + { + } + + member_database_type_id:: member_database_type_id (semantics::type* type, string const& fq_type, string const& key_prefix) - : relational::member_base (type, fq_type, key_prefix) + : member_base::base (type, fq_type, key_prefix), // virtual base + base (type, fq_type, key_prefix) { } @@ -469,6 +396,8 @@ namespace relational lob_database_id[mi.st->type - sql_type::BLOB]; } + entry member_database_type_id_; + // // query_columns // @@ -494,7 +423,7 @@ namespace relational { // For some types we need to pass precision and scale. // - sql_type const& st (column_sql_type (m)); + sql_type const& st (parse_sql_type (column_type (), m)); switch (st.type) { diff --git a/odb/relational/oracle/common.hxx b/odb/relational/oracle/common.hxx index 0f4517d..15120fd 100644 --- a/odb/relational/oracle/common.hxx +++ b/odb/relational/oracle/common.hxx @@ -12,118 +12,18 @@ namespace relational { namespace oracle { - struct member_base: virtual relational::member_base, context + struct member_base: virtual relational::member_base_impl, context { - member_base (base const& x): base (x) {} + member_base (base const& x): base (x), base_impl (x) {} // This c-tor is for the direct use inside the oracle namespace. // If you do use this c-tor, you should also explicitly call - // relational::member_base. + // relational::member_base (aka base). // member_base () {} - virtual void - traverse (semantics::data_member& m); - - struct member_info - { - semantics::data_member& m; // Member. - semantics::type& t; // Cvr-unqualified member C++ type, note - // that m.type () may not be the same as t. - semantics::type* wrapper; // Wrapper type if member is a composite or - // container wrapper, also cvr-unqualified. - // In this case t is the wrapped type. - bool cq; // True if the original (wrapper) type - // is const-qualified. - sql_type const* st; // Member SQL type (only simple values). - string& var; // Member variable name with trailing '_'. - - // C++ type fq-name. - // - string - fq_type (bool unwrap = true) const - { - semantics::names* hint; - - if (wrapper != 0 && unwrap) - { - // Use the hint from the wrapper unless the wrapped type - // is qualified. - // - hint = wrapper->get ("wrapper-hint"); - utype (*context::wrapper (*wrapper), hint); - return t.fq_name (hint); - } - - // Use the original type from 'm' instead of 't' since the hint - // may be invalid for a different type. Plus, if a type is - // overriden, then the fq_type must be as well. - // - if (fq_type_.empty ()) - { - semantics::type& t (utype (m, hint)); - return t.fq_name (hint); - } - else - return fq_type_; - } - - string const& fq_type_; - - member_info (semantics::data_member& m_, - semantics::type& t_, - semantics::type* wrapper_, - bool cq_, - string& var_, - string const& fq_type) - : m (m_), - t (t_), - wrapper (wrapper_), - cq (cq_), - st (0), - var (var_), - fq_type_ (fq_type) - { - } - }; - - bool - container (member_info& mi) - { - // This cannot be a container if we have a type override. - // - return type_override_ == 0 && context::container (mi.m); - } - - // The false return value indicates that no further callbacks - // should be called for this member. - // - virtual bool - pre (member_info&) - { - return true; - } - - virtual void - post (member_info&) - { - } - - virtual void - traverse_composite (member_info&) - { - } - - virtual void - traverse_container (member_info&) - { - } - - virtual void - traverse_object_pointer (member_info& mi) - { - traverse_simple (mi); - } + virtual sql_type const& + member_sql_type (semantics::data_member&); virtual void traverse_simple (member_info&); @@ -240,12 +140,16 @@ namespace relational string type_; }; - struct member_database_type_id: member_base + struct member_database_type_id: relational::member_database_type_id, + member_base { + member_database_type_id (base const&); + member_database_type_id (semantics::type* type = 0, string const& fq_type = string (), string const& key_prefix = string ()); - string + + virtual string database_type_id (type&); virtual void diff --git a/odb/relational/oracle/context.cxx b/odb/relational/oracle/context.cxx index 3d87729..dbe61af 100644 --- a/odb/relational/oracle/context.cxx +++ b/odb/relational/oracle/context.cxx @@ -159,17 +159,20 @@ namespace relational // sql_type const& context:: - column_sql_type (semantics::data_member& m, string const& kp) + parse_sql_type (string const& t, semantics::data_member& m) { - string key (kp.empty () - ? string ("oracle-column-sql-type") - : "oracle-" + kp + "-column-sql-type"); + // If this proves to be too expensive, we can maintain a + // cache of parsed types. + // + data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t)); - if (!m.count (key)) + if (i != data_->sql_type_cache_.end ()) + return i->second; + else { try { - m.set (key, parse_sql_type (column_type (m, kp))); + return (data_->sql_type_cache_[t] = parse_sql_type (t)); } catch (invalid_sql_type const& e) { @@ -179,8 +182,6 @@ namespace relational throw operation_failed (); } } - - return m.get (key); } sql_type context:: diff --git a/odb/relational/oracle/context.hxx b/odb/relational/oracle/context.hxx index 273f0e5..37716f0 100644 --- a/odb/relational/oracle/context.hxx +++ b/odb/relational/oracle/context.hxx @@ -5,6 +5,8 @@ #ifndef ODB_RELATIONAL_ORACLE_CONTEXT_HXX #define ODB_RELATIONAL_ORACLE_CONTEXT_HXX +#include + #include namespace relational @@ -73,8 +75,7 @@ namespace relational { public: sql_type const& - column_sql_type (semantics::data_member&, - string const& key_prefix = string ()); + parse_sql_type (string const&, semantics::data_member&); public: struct invalid_sql_type @@ -126,6 +127,9 @@ namespace relational struct data: base_context::data { data (std::ostream& os): base_context::data (os) {} + + typedef std::map sql_type_cache; + sql_type_cache sql_type_cache_; }; data* data_; }; diff --git a/odb/relational/oracle/model.cxx b/odb/relational/oracle/model.cxx index 347ea37..4b874c8 100644 --- a/odb/relational/oracle/model.cxx +++ b/odb/relational/oracle/model.cxx @@ -28,9 +28,7 @@ namespace relational { // Make sure the column is mapped to Oracle NUMBER. // - sql_type t (column_sql_type (m)); - - if (t.type != sql_type::NUMBER) + if (parse_sql_type (column_type (), m).type != sql_type::NUMBER) { cerr << m.file () << ":" << m.line () << ":" << m.column () << ": error: column with default value specified as C++ " diff --git a/odb/relational/oracle/source.cxx b/odb/relational/oracle/source.cxx index fb0984a..1d7d81f 100644 --- a/odb/relational/oracle/source.cxx +++ b/odb/relational/oracle/source.cxx @@ -316,187 +316,37 @@ namespace relational // init image // - struct init_image_member: relational::init_image_member, member_base + struct init_image_member: relational::init_image_member_impl, + member_base { init_image_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_database_type_id_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - // Ignore containers (they get their own table) and inverse - // object pointers (they are not present in this binding). - // - if (container (mi) || inverse (mi.m, key_prefix_)) - return false; - - if (!member_override_.empty ()) - member = member_override_; - else - { - // If we are generating standard init() and this member - // contains version, ignore it. - // - if (version (mi.m)) - return false; - - string const& name (mi.m.name ()); - member = "o." + name; - - os << "// " << name << endl - << "//" << endl; - - // If the whole class is readonly, then we will never be - // called with sk == statement_update. - // - 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_insert)"; - } - } - - // If this is a wrapped composite value, then we need to - // "unwrap" it. For simple values this is taken care of - // by the value_traits specializations. - // - if (mi.wrapper != 0 && composite (mi.t)) - { - // Here we need the wrapper type, not the wrapped type. - // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "get_ref (" + member + ")"; - } - - if (composite (mi.t)) - { - os << "{"; - traits = "composite_value_traits< " + mi.fq_type () + " >"; - } - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (semantics::class_* c = object_pointer (mt)) - { - type = "obj_traits::id_type"; - db_type_id = member_database_type_id_.database_type_id (mi.m); - - // Handle NULL pointers and extract the id. - // - os << "{" - << "typedef object_traits< " << class_fq_name (*c) << - " > obj_traits;"; - - if (weak_pointer (mt)) - { - os << "typedef pointer_traits< " << mi.fq_type () << - " > wptr_traits;" - << "typedef pointer_traits< wptr_traits::" << - "strong_pointer_type > ptr_traits;" - << endl - << "wptr_traits::strong_pointer_type sp (" << - "wptr_traits::lock (" << member << "));"; - - member = "sp"; - } - else - os << "typedef pointer_traits< " << mi.fq_type () << - " > ptr_traits;" - << endl; - - os << "bool is_null (ptr_traits::null_ptr (" << member << "));" - << "if (!is_null)" - << "{" - << "const " << type << "& id (" << endl; - - if (lazy_pointer (mt)) - os << "ptr_traits::object_id< ptr_traits::element_type > (" << - member << ")"; - else - os << "obj_traits::id (ptr_traits::get_ref (" << member << "))"; - - os << ");" - << endl; - - member = "id"; - } - else - { - type = mi.fq_type (); - db_type_id = member_database_type_id_.database_type_id (mi.m); - - os << "{" - << "bool is_null;"; - } - - traits = "oracle::value_traits<\n " - + type + ",\n " - + db_type_id + " >"; - } - - return true; - } - - virtual void - post (member_info& mi) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) { - if (composite (mi.t)) - os << "}"; - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - if (object_pointer (member_utype (mi.m, key_prefix_))) - { - os << "}"; - - if (!null (mi.m, key_prefix_)) - os << "else" << endl - << "throw null_pointer ();"; - } - - os << "i." << mi.var << "indicator = is_null ? -1 : 0;" - << "}"; - } } virtual void - traverse_composite (member_info& mi) + set_null (member_info& mi) { - os << traits << "::init (" << endl - << "i." << mi.var << "value," << endl - << member << "," << endl - << "sk);"; + os << "i." << mi.var << "indicator = -1;"; } virtual void traverse_int32 (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; } virtual void traverse_int64 (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; } virtual void @@ -508,6 +358,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;" << "i." << mi.var << "size = static_cast (size);"; } @@ -515,14 +366,16 @@ namespace relational traverse_float (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; } virtual void traverse_double (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; } virtual void @@ -535,6 +388,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;" << "i." << mi.var << "size = static_cast (size);"; } @@ -542,28 +396,32 @@ namespace relational traverse_date (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; } virtual void traverse_timestamp (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; } virtual void traverse_interval_ym (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; } virtual void traverse_interval_ds (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; } virtual void @@ -576,6 +434,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;" << "i." << mi.var << "size = static_cast (size);"; } @@ -587,16 +446,9 @@ namespace relational << "i." << mi.var << "callback.callback.param," << endl << "i." << mi.var << "callback.context.param," << endl << "is_null," << endl - << member << ");"; + << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; } - - private: - string type; - string db_type_id; - string member; - string traits; - - member_database_type_id member_database_type_id_; }; entry init_image_member_; @@ -604,147 +456,21 @@ namespace relational // init value // - struct init_value_member: relational::init_value_member, member_base + struct init_value_member: relational::init_value_member_impl, + member_base { init_value_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_database_type_id_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - if (!member_override_.empty ()) - member = member_override_; - else - { - string const& name (mi.m.name ()); - member = "o." + name; - - if (mi.cq) - member = "const_cast< " + mi.fq_type (false) + "& > (" + - member + ")"; - - os << "// " << name << endl - << "//" << endl; - } - - // If this is a wrapped composite value, then we need to - // "unwrap" it. For simple values this is taken care of - // by the value_traits specializations. - // - if (mi.wrapper != 0 && composite (mi.t)) - { - // Here we need the wrapper type, not the wrapped type. - // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "set_ref (\n" + member + ")"; - } - - if (composite (mi.t)) - traits = "composite_value_traits< " + mi.fq_type () + " >"; - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (semantics::class_* c = object_pointer (mt)) - { - type = "obj_traits::id_type"; - db_type_id = member_database_type_id_.database_type_id (mi.m); - - // Handle NULL pointers and extract the id. - // - os << "{" - << "typedef object_traits< " << class_fq_name (*c) << - " > obj_traits;" - << "typedef pointer_traits< " << mi.fq_type () << - " > ptr_traits;" - << endl - << "if (i." << mi.var << "indicator == -1)" << endl; - - if (null (mi.m, key_prefix_)) - os << member << " = ptr_traits::pointer_type ();"; - else - os << "throw null_pointer ();"; - - os << "else" - << "{" - << type << " id;"; - - member = "id"; - } - else - { - type = mi.fq_type (); - db_type_id = member_database_type_id_.database_type_id (mi.m); - } - - traits = "oracle::value_traits<\n " - + type + ",\n " - + db_type_id + " >"; - } - - return true; - } - - virtual void - post (member_info& mi) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) { - if (composite (mi.t)) - return; - - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (object_pointer (mt)) - { - if (!member_override_.empty ()) - member = member_override_; - else - { - member = "o." + mi.m.name (); - - if (mi.cq) - member = "const_cast< " + mi.fq_type (false) + "& > (" + - member + ")"; - } - - if (lazy_pointer (mt)) - os << member << " = ptr_traits::pointer_type (db, id);"; - else - os << "// If a compiler error points to the line below, then" << endl - << "// it most likely means that a pointer used in a member" << endl - << "// cannot be initialized from an object pointer." << endl - << "//" << endl - << member << " = ptr_traits::pointer_type (" << endl - << "db.load< obj_traits::object_type > (id));"; - - os << "}" - << "}"; - } } virtual void - traverse_composite (member_info& mi) + get_null (member_info& mi) { - os << traits << "::init (" << endl - << member << "," << endl - << "i." << mi.var << "value," << endl - << "db);" - << endl; + os << "i." << mi.var << "indicator == -1"; } virtual void @@ -870,14 +596,6 @@ namespace relational << "i." << mi.var << "indicator == -1);" << endl; } - - private: - string type; - string db_type_id; - string traits; - string member; - - member_database_type_id member_database_type_id_; }; entry init_value_member_; diff --git a/odb/relational/pgsql/common.cxx b/odb/relational/pgsql/common.cxx index 0ec9f7c..3d7e01e 100644 --- a/odb/relational/pgsql/common.cxx +++ b/odb/relational/pgsql/common.cxx @@ -16,91 +16,10 @@ namespace relational // member_base // - void member_base:: - traverse (semantics::data_member& m) + sql_type const& member_base:: + member_sql_type (semantics::data_member& m) { - if (transient (m)) - return; - - string var; - - if (!var_override_.empty ()) - var = var_override_; - else - { - string const& name (m.name ()); - var = name + (name[name.size () - 1] == '_' ? "" : "_"); - } - - bool cq (type_override_ != 0 ? false : const_type (m.type ())); - semantics::type& t (type_override_ != 0 ? *type_override_ : utype (m)); - - semantics::type* cont; - if (semantics::class_* c = composite_wrapper (t)) - { - // If t is a wrapper, pass the wrapped type. Also pass the - // original, wrapper type. - // - member_info mi (m, - *c, - (wrapper (t) ? &t : 0), - cq, - var, - fq_type_override_); - if (pre (mi)) - { - traverse_composite (mi); - post (mi); - } - } - // This cannot be a container if we have a type override. - // - else if (type_override_ == 0 && (cont = context::container (m))) - { - // The same unwrapping logic as for composite values. - // - member_info mi (m, - *cont, - (wrapper (t) ? &t : 0), - cq, - var, - fq_type_override_); - if (pre (mi)) - { - traverse_container (mi); - post (mi); - } - } - else - { - sql_type const& st (column_sql_type (m, key_prefix_)); - - if (semantics::class_* c = object_pointer (t)) - { - member_info mi (m, - utype (*id_member (*c)), - 0, - cq, - var, - fq_type_override_); - mi.st = &st; - if (pre (mi)) - { - traverse_object_pointer (mi); - post (mi); - } - } - else - { - member_info mi (m, t, 0, cq, var, fq_type_override_); - mi.st = &st; - if (pre (mi)) - { - traverse_simple (mi); - post (mi); - } - } - } + return parse_sql_type (column_type (m, key_prefix_), m); } void member_base:: @@ -310,13 +229,21 @@ namespace relational "id_string", // TEXT, "id_bytea" // BYTEA }; - } + } + + member_database_type_id:: + member_database_type_id (base const& x) + : member_base::base (x), // virtual base + base (x) + { + } member_database_type_id:: member_database_type_id (semantics::type* type, string const& fq_type, string const& key_prefix) - : relational::member_base (type, fq_type, key_prefix) + : member_base::base (type, fq_type, key_prefix), // virtual base + base (type, fq_type, key_prefix) { } @@ -386,6 +313,8 @@ namespace relational type_id_ = "pgsql::id_uuid"; } + entry member_database_type_id_; + // // query_columns // diff --git a/odb/relational/pgsql/common.hxx b/odb/relational/pgsql/common.hxx index 4f3ee4d..4b9bbd5 100644 --- a/odb/relational/pgsql/common.hxx +++ b/odb/relational/pgsql/common.hxx @@ -12,118 +12,18 @@ namespace relational { namespace pgsql { - struct member_base: virtual relational::member_base, context + struct member_base: virtual relational::member_base_impl, context { - member_base (base const& x): base (x) {} + member_base (base const& x): base (x), base_impl (x) {} // This c-tor is for the direct use inside the pgsql namespace. // If you do use this c-tor, you should also explicitly call - // relational::member_base. + // relational::member_base (aka base). // member_base () {} - virtual void - traverse (semantics::data_member& m); - - struct member_info - { - semantics::data_member& m; // Member. - semantics::type& t; // Cvr-unqualified member C++ type, note - // that m.type () may not be the same as t. - semantics::type* wrapper; // Wrapper type if member is a composite or - // container wrapper, also cvr-unqualified. - // In this case t is the wrapped type. - bool cq; // True if the original (wrapper) type - // is const-qualified. - sql_type const* st; // Member SQL type (only simple values). - string& var; // Member variable name with trailing '_'. - - // C++ type fq-name. - // - string - fq_type (bool unwrap = true) const - { - semantics::names* hint; - - if (wrapper != 0 && unwrap) - { - // Use the hint from the wrapper unless the wrapped type - // is qualified. - // - hint = wrapper->get ("wrapper-hint"); - utype (*context::wrapper (*wrapper), hint); - return t.fq_name (hint); - } - - // Use the original type from 'm' instead of 't' since the hint - // may be invalid for a different type. Plus, if a type is - // overriden, then the fq_type must be as well. - // - if (fq_type_.empty ()) - { - semantics::type& t (utype (m, hint)); - return t.fq_name (hint); - } - else - return fq_type_; - } - - string const& fq_type_; - - member_info (semantics::data_member& m_, - semantics::type& t_, - semantics::type* wrapper_, - bool cq_, - string& var_, - string const& fq_type) - : m (m_), - t (t_), - wrapper (wrapper_), - cq (cq_), - st (0), - var (var_), - fq_type_ (fq_type) - { - } - }; - - bool - container (member_info& mi) - { - // This cannot be a container if we have a type override. - // - return type_override_ == 0 && context::container (mi.m); - } - - // The false return value indicates that no further callbacks - // should be called for this member. - // - virtual bool - pre (member_info&) - { - return true; - } - - virtual void - post (member_info&) - { - } - - virtual void - traverse_composite (member_info&) - { - } - - virtual void - traverse_container (member_info&) - { - } - - virtual void - traverse_object_pointer (member_info& mi) - { - traverse_simple (mi); - } + virtual sql_type const& + member_sql_type (semantics::data_member&); virtual void traverse_simple (member_info&); @@ -208,12 +108,16 @@ namespace relational string type_; }; - struct member_database_type_id: member_base + struct member_database_type_id: relational::member_database_type_id, + member_base { + member_database_type_id (base const&); + member_database_type_id (semantics::type* type = 0, string const& fq_type = string (), string const& key_prefix = string ()); - string + + virtual string database_type_id (type&); virtual void diff --git a/odb/relational/pgsql/context.cxx b/odb/relational/pgsql/context.cxx index 8cc4eca..17ceaaa 100644 --- a/odb/relational/pgsql/context.cxx +++ b/odb/relational/pgsql/context.cxx @@ -236,17 +236,20 @@ namespace relational // sql_type const& context:: - column_sql_type (semantics::data_member& m, string const& kp) + parse_sql_type (string const& t, semantics::data_member& m) { - string key (kp.empty () - ? string ("pgsql-column-sql-type") - : "pgsql-" + kp + "-column-sql-type"); + // If this proves to be too expensive, we can maintain a + // cache of parsed types. + // + data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t)); - if (!m.count (key)) + if (i != data_->sql_type_cache_.end ()) + return i->second; + else { try { - m.set (key, parse_sql_type (column_type (m, kp))); + return (data_->sql_type_cache_[t] = parse_sql_type (t)); } catch (invalid_sql_type const& e) { @@ -256,8 +259,6 @@ namespace relational throw operation_failed (); } } - - return m.get (key); } sql_type context:: diff --git a/odb/relational/pgsql/context.hxx b/odb/relational/pgsql/context.hxx index a79cceb..1da353a 100644 --- a/odb/relational/pgsql/context.hxx +++ b/odb/relational/pgsql/context.hxx @@ -5,6 +5,8 @@ #ifndef ODB_RELATIONAL_PGSQL_CONTEXT_HXX #define ODB_RELATIONAL_PGSQL_CONTEXT_HXX +#include + #include namespace relational @@ -69,8 +71,7 @@ namespace relational { public: sql_type const& - column_sql_type (semantics::data_member&, - string const& key_prefix = string ()); + parse_sql_type (string const&, semantics::data_member&); public: struct invalid_sql_type @@ -124,6 +125,9 @@ namespace relational struct data: base_context::data { data (std::ostream& os): base_context::data (os) {} + + typedef std::map sql_type_cache; + sql_type_cache sql_type_cache_; }; data* data_; }; diff --git a/odb/relational/pgsql/model.cxx b/odb/relational/pgsql/model.cxx index ebd04af..06a7dde 100644 --- a/odb/relational/pgsql/model.cxx +++ b/odb/relational/pgsql/model.cxx @@ -34,7 +34,7 @@ namespace relational { // Make sure the column is mapped to an integer type. // - switch (column_sql_type (m).type) + switch (parse_sql_type (column_type (), m).type) { case sql_type::SMALLINT: case sql_type::INTEGER: diff --git a/odb/relational/pgsql/source.cxx b/odb/relational/pgsql/source.cxx index ceb7dbd..ae19e05 100644 --- a/odb/relational/pgsql/source.cxx +++ b/odb/relational/pgsql/source.cxx @@ -103,7 +103,21 @@ namespace relational struct statement_oids: object_columns_base, context { - statement_oids (statement_kind sk): sk_ (sk) {} + statement_oids (statement_kind sk, bool first = true) + : object_columns_base (first), sk_ (sk) + { + } + + virtual void + traverse_pointer (semantics::data_member& m, semantics::class_& c) + { + // Ignore certain columns depending on what kind statement we are + // generating. See object_columns in common source generator for + // details. + // + if (!(inverse (m, key_prefix_) && sk_ != statement_select)) + object_columns_base::traverse_pointer (m, c); + } virtual bool traverse_column (semantics::data_member& m, @@ -114,10 +128,7 @@ namespace relational // generating. See object_columns in common source generator for // details. // - if (inverse (m) && sk_ != statement_select) - return false; - - if ((id (m) || readonly (member_path_, member_scope_)) && + if ((id () || readonly (member_path_, member_scope_)) && sk_ == statement_update) return false; @@ -128,7 +139,7 @@ namespace relational if (!first) os << ',' << endl; - os << oids[column_sql_type (m).type]; + os << oids[parse_sql_type (column_type (), m).type]; return true; } @@ -471,188 +482,37 @@ namespace relational // init image // - struct init_image_member: relational::init_image_member, member_base + struct init_image_member: relational::init_image_member_impl, + member_base { init_image_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_database_type_id_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - // Ignore containers (they get their own table) and inverse - // object pointers (they are not present in this binding). - // - if (container (mi) || inverse (mi.m, key_prefix_)) - return false; - - if (!member_override_.empty ()) - member = member_override_; - else - { - // If we are generating standard init() and this member - // contains version, ignore it. - // - if (version (mi.m)) - return false; - - string const& name (mi.m.name ()); - member = "o." + name; - - os << "// " << name << endl - << "//" << endl; - - // If the whole class is readonly, then we will never be - // called with sk == statement_update. - // - 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_insert)"; - } - } - - // If this is a wrapped composite value, then we need to - // "unwrap" it. For simple values this is taken care of - // by the value_traits specializations. - // - if (mi.wrapper != 0 && composite (mi.t)) - { - // Here we need the wrapper type, not the wrapped type. - // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "get_ref (" + member + ")"; - } - - if (composite (mi.t)) - { - os << "{"; - traits = "composite_value_traits< " + mi.fq_type () + " >"; - } - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (semantics::class_* c = object_pointer (mt)) - { - type = "obj_traits::id_type"; - db_type_id = member_database_type_id_.database_type_id (mi.m); - - // Handle NULL pointers and extract the id. - // - os << "{" - << "typedef object_traits< " << class_fq_name (*c) << - " > obj_traits;"; - - if (weak_pointer (mt)) - { - os << "typedef pointer_traits< " << mi.fq_type () << - " > wptr_traits;" - << "typedef pointer_traits< wptr_traits::" << - "strong_pointer_type > ptr_traits;" - << endl - << "wptr_traits::strong_pointer_type sp (" << - "wptr_traits::lock (" << member << "));"; - - member = "sp"; - } - else - os << "typedef pointer_traits< " << mi.fq_type () << - " > ptr_traits;" - << endl; - - os << "bool is_null (ptr_traits::null_ptr (" << member << "));" - << "if (!is_null)" - << "{" - << "const " << type << "& id (" << endl; - - if (lazy_pointer (mt)) - os << "ptr_traits::object_id< ptr_traits::element_type > (" << - member << ")"; - else - os << "obj_traits::id (ptr_traits::get_ref (" << member << "))"; - - os << ");" - << endl; - - member = "id"; - } - else - { - type = mi.fq_type (); - db_type_id = member_database_type_id_.database_type_id (mi.m); - - os << "{" - << "bool is_null;"; - } - - traits = "pgsql::value_traits<\n " - + type + ",\n " - + db_type_id + " >"; - } - - return true; - } - - virtual void - post (member_info& mi) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) { - if (composite (mi.t)) - os << "}"; - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - if (object_pointer (member_utype (mi.m, key_prefix_))) - { - os << "}"; - - if (!null (mi.m, key_prefix_)) - os << "else" << endl - << "throw null_pointer ();"; - } - - os << "i." << mi.var << "null = is_null;" - << "}"; - } } virtual void - traverse_composite (member_info& mi) + set_null (member_info& mi) { - os << "if (" << traits << "::init (" << endl - << "i." << mi.var << "value," << endl - << member << "," << endl - << "sk))" << endl - << "grew = true;"; + os << "i." << mi.var << "null = true;"; } virtual void traverse_integer (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "null = is_null;"; } virtual void traverse_float (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "null = is_null;"; } virtual void @@ -667,6 +527,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "null = is_null;" << "i." << mi.var << "size = size;" << "grew = grew || (cap != i." << mi.var << "value.capacity ());"; } @@ -675,7 +536,8 @@ namespace relational traverse_date_time (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "null = is_null;"; } virtual void @@ -688,6 +550,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "null = is_null;" << "i." << mi.var << "size = size;" << "grew = grew || (cap != i." << mi.var << "value.capacity ());"; } @@ -702,6 +565,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "null = is_null;" << "i." << mi.var << "size = size;"; } @@ -715,6 +579,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "null = is_null;" << "i." << mi.var << "size = size;" << "grew = grew || (cap != i." << mi.var << "value.capacity ());"; } @@ -723,16 +588,9 @@ namespace relational traverse_uuid (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "null = is_null;"; } - - private: - string type; - string db_type_id; - string member; - string traits; - - member_database_type_id member_database_type_id_; }; entry init_image_member_; @@ -740,147 +598,21 @@ namespace relational // init value // - struct init_value_member: relational::init_value_member, member_base + struct init_value_member: relational::init_value_member_impl, + member_base { init_value_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_database_type_id_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - if (!member_override_.empty ()) - member = member_override_; - else - { - string const& name (mi.m.name ()); - member = "o." + name; - - if (mi.cq) - member = "const_cast< " + mi.fq_type (false) + "& > (" + - member + ")"; - - os << "// " << name << endl - << "//" << endl; - } - - // If this is a wrapped composite value, then we need to - // "unwrap" it. For simple values this is taken care of - // by the value_traits specializations. - // - if (mi.wrapper != 0 && composite (mi.t)) - { - // Here we need the wrapper type, not the wrapped type. - // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "set_ref (\n" + member + ")"; - } - - if (composite (mi.t)) - traits = "composite_value_traits< " + mi.fq_type () + " >"; - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (semantics::class_* c = object_pointer (mt)) - { - type = "obj_traits::id_type"; - db_type_id = member_database_type_id_.database_type_id (mi.m); - - // Handle NULL pointers and extract the id. - // - os << "{" - << "typedef object_traits< " << class_fq_name (*c) << - " > obj_traits;" - << "typedef pointer_traits< " << mi.fq_type () << - " > ptr_traits;" - << endl - << "if (i." << mi.var << "null)" << endl; - - if (null (mi.m, key_prefix_)) - os << member << " = ptr_traits::pointer_type ();"; - else - os << "throw null_pointer ();"; - - os << "else" - << "{" - << type << " id;"; - - member = "id"; - } - else - { - type = mi.fq_type (); - db_type_id = member_database_type_id_.database_type_id (mi.m); - } - - traits = "pgsql::value_traits<\n " - + type + ",\n " - + db_type_id + " >"; - } - - return true; - } - - virtual void - post (member_info& mi) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) { - if (composite (mi.t)) - return; - - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (object_pointer (mt)) - { - if (!member_override_.empty ()) - member = member_override_; - else - { - member = "o." + mi.m.name (); - - if (mi.cq) - member = "const_cast< " + mi.fq_type (false) + "& > (" + - member + ")"; - } - - if (lazy_pointer (mt)) - os << member << " = ptr_traits::pointer_type (db, id);"; - else - os << "// If a compiler error points to the line below, then" << endl - << "// it most likely means that a pointer used in a member" << endl - << "// cannot be initialized from an object pointer." << endl - << "//" << endl - << member << " = ptr_traits::pointer_type (" << endl - << "db.load< obj_traits::object_type > (id));"; - - os << "}" - << "}"; - } } virtual void - traverse_composite (member_info& mi) + get_null (member_info& mi) { - os << traits << "::init (" << endl - << member << "," << endl - << "i." << mi.var << "value," << endl - << "db);" - << endl; + os << "i." << mi.var << "null"; } virtual void @@ -970,14 +702,6 @@ namespace relational << "i." << mi.var << "null);" << endl; } - - private: - string type; - string db_type_id; - string traits; - string member; - - member_database_type_id member_database_type_id_; }; entry init_value_member_; @@ -1090,7 +814,7 @@ namespace relational << "{"; instance st (statement_select); - st->traverse_column (*id, "", true); + st->traverse (*id); os << "};"; } @@ -1111,16 +835,11 @@ namespace relational bool first (cc.total == cc.id + cc.inverse + cc.readonly + cc.optimistic_managed); - { - instance st (statement_where); - st->traverse_column (*id, "", first); - } + instance st (statement_where, first); + st->traverse (*id); if (optimistic != 0) - { - instance st (statement_where); - st->traverse_column (*optimistic, "", false); - } + st->traverse (*optimistic); os << "};"; } @@ -1134,7 +853,7 @@ namespace relational << "{"; instance st (statement_where); - st->traverse_column (*id, "", true); + st->traverse (*id); os << "};"; } @@ -1145,15 +864,9 @@ namespace relational << "optimistic_erase_statement_types[] =" << "{"; - { - instance st (statement_where); - st->traverse_column (*id, "", true); - } - - { - instance st (statement_where); - st->traverse_column (*optimistic, "", false); - } + instance st (statement_where); + st->traverse (*id); + st->traverse (*optimistic); os << "};"; } @@ -1258,8 +971,7 @@ namespace relational bool inv (inv_m != 0); semantics::type& vt (container_vt (t)); - - string id_oid (oids[column_sql_type (m, "id").type]); + semantics::type& idt (container_idt (m)); // select_all statement types. // @@ -1268,20 +980,22 @@ namespace relational << "select_all_types[] =" << "{"; + instance so (statement_where); + if (inv) { // many(i)-to-many // if (container (*inv_m)) - os << oids[column_sql_type (*inv_m, "value").type]; + so->traverse (*inv_m, idt, "value", "value"); // many(i)-to-one // else - os << oids[column_sql_type (*inv_m).type]; + so->traverse (*inv_m); } else - os << id_oid; + so->traverse (m, idt, "id", "object_id"); os << "};"; } @@ -1295,30 +1009,22 @@ namespace relational if (!inv) { - os << id_oid << ","; + instance so (statement_insert); + + so->traverse (m, idt, "id", "object_id"); switch (container_kind (t)) { case ck_ordered: { if (!unordered (m)) - os << oids[column_sql_type (m, "index").type] << ","; - + so->traverse (m, container_it (t), "index", "index"); break; } case ck_map: case ck_multimap: { - if (semantics::class_* ktc = - composite_wrapper (container_kt (t))) - { - instance st (statement_insert); - st->traverse (m, *ktc, "key", "key"); - os << ","; - } - else - os << oids[column_sql_type (m, "key").type] << ","; - + so->traverse (m, container_kt (t), "key", "key"); break; } case ck_set: @@ -1328,14 +1034,7 @@ namespace relational } } - if (semantics::class_* vtc = composite_wrapper (vt)) - { - instance st (statement_insert); - st->traverse (m, *vtc, "value", "value"); - } - else - os << oids[column_sql_type (m, "value").type]; - + so->traverse (m, vt, "value", "value"); } else // MSVC does not allow zero length arrays or uninitialized @@ -1354,7 +1053,10 @@ namespace relational << "{"; if (!inv) - os << id_oid; + { + instance so (statement_where); + so->traverse (m, idt, "id", "object_id"); + } else os << "0"; diff --git a/odb/relational/processor.cxx b/odb/relational/processor.cxx index 8c18f02..0bede14 100644 --- a/odb/relational/processor.cxx +++ b/odb/relational/processor.cxx @@ -142,7 +142,7 @@ namespace relational // const auto_ptr - can modify by changing the pointed-to value // if (const_type (m.type ()) && - !(m.count ("id") || m.count ("version") || m.count ("inverse"))) + !(id (m) || version (m) || m.count ("inverse"))) { if (qwt == 0 || const_type (*qwt)) m.set ("readonly", true); @@ -153,11 +153,19 @@ namespace relational if (composite_wrapper (t)) return; - string type, ref_type; + string type, id_type; + + if (m.count ("id-type")) + id_type = m.get ("id-type"); if (m.count ("type")) + { type = m.get ("type"); + if (id_type.empty ()) + id_type = type; + } + if (semantics::class_* c = process_object_pointer (m, t)) { // This is an object pointer. The column type is the pointed-to @@ -168,26 +176,55 @@ namespace relational semantics::names* idhint; semantics::type& idt (utype (id, idhint)); + semantics::type* wt (0); + semantics::names* whint (0); + if (process_wrapper (idt)) + { + whint = idt.get ("wrapper-hint"); + wt = &utype (*idt.get ("wrapper-type"), whint); + } + + // Nothing to do if this is a composite value type. + // + if (composite_wrapper (idt)) + return; + + if (type.empty () && id.count ("id-type")) + type = id.get ("id-type"); + if (type.empty () && id.count ("type")) type = id.get ("type"); + // The rest should be identical to the code for the id_type in + // the else block. + // if (type.empty () && idt.count ("id-type")) type = idt.get ("id-type"); + if (type.empty () && wt != 0 && wt->count ("id-type")) + type = wt->get ("id-type"); + if (type.empty () && idt.count ("type")) type = idt.get ("type"); + if (type.empty () && wt != 0 && wt->count ("type")) + type = wt->get ("type"); + if (type.empty ()) type = database_type (idt, idhint, true); + + if (type.empty () && wt != 0) + type = database_type (*wt, whint, true); + + id_type = type; } else { - if (type.empty () && m.count ("id") && t.count ("id-type")) - type = t.get ("id-type"); + if (id_type.empty () && t.count ("id-type")) + id_type = t.get ("id-type"); - if (type.empty () && wt != 0 && m.count ("id") && - wt->count ("id-type")) - type = wt->get ("id-type"); + if (id_type.empty () && wt != 0 && wt->count ("id-type")) + id_type = wt->get ("id-type"); if (type.empty () && t.count ("type")) type = t.get ("type"); @@ -195,16 +232,29 @@ namespace relational if (type.empty () && wt != 0 && wt->count ("type")) type = wt->get ("type"); + if (id_type.empty ()) + id_type = type; + + if (id_type.empty ()) + id_type = database_type (t, hint, true); + if (type.empty ()) - type = database_type (t, hint, m.count ("id")); + type = database_type (t, hint, false); + + if (id_type.empty () && wt != 0) + id_type = database_type (*wt, whint, true); if (type.empty () && wt != 0) - type = database_type (*wt, whint, m.count ("id")); + type = database_type (*wt, whint, false); + + if (id (m)) + type = id_type; } if (!type.empty ()) { m.set ("column-type", type); + m.set ("column-id-type", id_type); // Issue a warning if we are relaxing null-ness. // @@ -274,24 +324,53 @@ namespace relational if (obj_ptr && (c = process_object_pointer (m, t, prefix))) { // This is an object pointer. The column type is the pointed-to - // object id type. Except by default it can be NULL. + // object id type. // semantics::data_member& id (*id_member (*c)); - semantics::names* hint; - semantics::type& idt (utype (id, hint)); + semantics::names* idhint; + semantics::type& idt (utype (id, idhint)); + + semantics::type* wt (0); + semantics::names* whint (0); + if (process_wrapper (idt)) + { + whint = idt.get ("wrapper-hint"); + wt = &utype (*idt.get ("wrapper-type"), whint); + } + + // Nothing to do if this is a composite value type. + // + if (composite_wrapper (idt)) + return; + + if (type.empty () && id.count ("id-type")) + type = id.get ("id-type"); if (type.empty () && id.count ("type")) type = id.get ("type"); + // The rest of the code is identical to the else block except here + // we have to check for "id-type" before checking for "type". + // + if (type.empty () && idt.count ("id-type")) type = idt.get ("id-type"); + if (type.empty () && wt != 0 && wt->count ("id-type")) + type = wt->get ("id-type"); + if (type.empty () && idt.count ("type")) type = idt.get ("type"); + if (type.empty () && wt != 0 && wt->count ("type")) + type = wt->get ("type"); + if (type.empty ()) - type = database_type (idt, hint, true); + type = database_type (idt, idhint, true); + + if (type.empty () && wt != 0) + type = database_type (*wt, whint, true); } else { @@ -311,6 +390,7 @@ namespace relational if (!type.empty ()) { m.set (prefix + "-column-type", type); + m.set (prefix + "-column-id-type", type); return; } @@ -1094,7 +1174,7 @@ namespace relational TREE_VEC_ELT (args, 0) = arg; // This step should succeed regardles of whether there is a - // container traits specialization for this type. + // specialization for this type. // tree inst ( lookup_template_class (t, args, 0, 0, 0, tf_warning_or_error)); @@ -1939,30 +2019,27 @@ namespace relational } virtual void - traverse_simple (semantics::data_member& m) + traverse_pointer (semantics::data_member& m, semantics::class_& c) { - if (semantics::class_* c = object_pointer (utype (m))) - { - // 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. - // - if (inverse (m)) - 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. + // + if (inverse (m)) + return; - // Ignore self-pointers if requested. - // - if (!self_pointer_ && pointer_->obj == c) - return; + // Ignore self-pointers if requested. + // + if (!self_pointer_ && pointer_->obj == &c) + return; - if (pointee_.obj == c) - { - relationships_.push_back (relationship ()); - relationships_.back ().member = &m; - relationships_.back ().name = member_prefix_ + m.name (); - relationships_.back ().pointer = pointer_; - relationships_.back ().pointee = &pointee_; - } + if (pointee_.obj == &c) + { + relationships_.push_back (relationship ()); + relationships_.back ().member = &m; + relationships_.back ().name = member_prefix_ + m.name (); + relationships_.back ().pointer = pointer_; + relationships_.back ().pointee = &pointee_; } } diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx index 52ff093..7f94410 100644 --- a/odb/relational/source.hxx +++ b/odb/relational/source.hxx @@ -28,13 +28,15 @@ namespace relational { statement_column (): member (0) {} statement_column (std::string const& c, + std::string const& t, semantics::data_member& m, std::string const& kp = "") - : column (c), member (&m), key_prefix (kp) + : column (c), type (t), member (&m), key_prefix (kp) { } - std::string column; + std::string column; // Column name. + std::string type; // Column SQL type. semantics::data_member* member; std::string key_prefix; }; @@ -83,73 +85,108 @@ namespace relational { } - virtual bool - traverse_column (semantics::data_member& m, string const& name, bool) + virtual void + traverse_pointer (semantics::data_member& m, semantics::class_& c) { - semantics::data_member* im (inverse (m)); + semantics::data_member* im (inverse (m, key_prefix_)); // Ignore certain columns depending on what kind statement we are // generating. Columns corresponding to the inverse members are - // only present in the select statements while the id and readonly - // columns are not present in the update statements. + // only present in the select statements. // if (im != 0 && sk_ != statement_select) - return false; - - if ((id (m) || readonly (member_path_, member_scope_)) && - sk_ == statement_update) - return false; + return; // Inverse object pointers come from a joined table. // if (im != 0) { - semantics::class_* c (object_pointer (utype (m))); + semantics::data_member& id (*id_member (c)); + semantics::type& idt (utype (id)); if (container (*im)) { // This container is a direct member of the class so the table // prefix is just the class table name. We don't assign join // aliases for container tables so use the actual table name. - // Note that the (table_name_.empty () ? :) test may look wrong - // at first but it is no. + // Note that the if(!table_name_.empty ()) test may look wrong + // at first but it is not; if table_name_ is empty then we are + // generating a container table where we don't qualify columns + // with tables. // - column ( - *im, - "id", - table_name_.empty () - ? table_name_ - : table_qname (*im, - table_prefix (schema (c->scope ()), - table_name (*c) + "_", - 1)), - column_qname (*im, "id", "object_id")); + string table; + + if (!table_name_.empty ()) + { + table_prefix tp (schema (c.scope ()), table_name (c) + "_", 1); + table = table_qname (*im, tp); + } + + instance oc (table, sk_, sc_); + oc->traverse (*im, idt, "id", "object_id", &c); } else { - semantics::data_member& id (*id_member (*c)); - - // Use the join alias (column name) instead of the actual - // table name unless we are handling a container. Note that - // the (table_name_.empty () ? :) test may look wrong at - // first but it is no. + // Use the join alias instead of the actual table name unless we + // are handling a container. Generally, we want the join alias + // 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. Note that the + // if(!table_name_.empty ()) test may look wrong at first but + // it is not; if table_name_ is empty then we are generating a + // container table where we don't qualify columns with tables. // - column ( - id, - "", - table_name_.empty () ? table_name_ : quote_id (name), - column_qname (id)); + string table; + + if (!table_name_.empty ()) + { + if (composite_wrapper (idt)) + { + 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. + + table = column_prefix_ + p; + } + else + table = column_prefix_ + + column_name (m, key_prefix_, default_name_); + + table = quote_id (table); + } + + instance oc (table, sk_, sc_); + oc->traverse (id); } } else - column (m, "", table_name_, quote_id (name)); + object_columns_base::traverse_pointer (m, c); + } + + virtual bool + traverse_column (semantics::data_member& m, string const& name, bool) + { + // Ignore certain columns depending on what kind statement we are + // generating. Id and readonly columns are not present in the update + // statements. + // + if ((id () || readonly (member_path_, member_scope_)) && + sk_ == statement_update) + return false; + + column (m, table_name_, quote_id (name)); return true; } virtual void column (semantics::data_member& m, - string const& key_prefix, string const& table, string const& column) { @@ -177,7 +214,7 @@ namespace relational r += param_->next (); } - sc_.push_back (statement_column (r, m, key_prefix)); + sc_.push_back (statement_column (r, column_type (), m, key_prefix_)); } protected: @@ -350,7 +387,7 @@ namespace relational virtual void column (semantics::data_member& m, string const& column) { - sc_.push_back (statement_column (column, m)); + sc_.push_back (statement_column (column, column_type (), m)); } protected: @@ -370,6 +407,7 @@ namespace relational table_ (table_qname (scope)), id_ (*id_member (scope)) { + id_cols_->traverse (id_); } size_t @@ -399,31 +437,63 @@ namespace relational } } - virtual bool - traverse_column (semantics::data_member& m, string const& column, bool) + virtual void + traverse_pointer (semantics::data_member& m, semantics::class_& c) { - semantics::class_* c (object_pointer (utype (m))); - - if (c == 0) - return false; - string t, a, dt, da; std::ostringstream cond, dcond; // @@ diversion? - if (semantics::data_member* im = inverse (m)) + // Derive table alias for this member. Generally, we want the + // alias 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_); + + if (semantics::data_member* im = inverse (m, key_prefix_)) { if (container (*im)) { // This container is a direct member of the class so the table // prefix is just the class table name. // - qname const& ct (table_name (*c)); - table_prefix tp (schema (c->scope ()), ct + "_", 1); + qname const& ct (table_name (c)); + table_prefix tp (schema (c.scope ()), ct + "_", 1); t = table_qname (*im, tp); - string const& val (column_qname (*im, "value", "value")); - cond << t << '.' << val << " = " << - table_ << "." << column_qname (id_); + // Container's value is our id. + // + instance id_cols; + id_cols->traverse (*im, utype (id_), "value", "value"); + + for (object_columns_list::iterator b (id_cols->begin ()), i (b), + j (id_cols_->begin ()); i != id_cols->end (); ++i, ++j) + { + + if (i != b) + cond << " AND "; + + cond << t << '.' << quote_id (i->name) << '=' << + table_ << '.' << quote_id (j->name); + } // Add the join for the object itself so that we are able to // use it in the WHERE clause. @@ -431,21 +501,44 @@ namespace relational if (query_) { dt = quote_id (ct); - da = quote_id (column); + da = quote_id (alias); + + semantics::data_member& id (*id_member (c)); + + instance oid_cols, cid_cols; + oid_cols->traverse (id); + cid_cols->traverse (*im, utype (id), "id", "object_id", &c); + + for (object_columns_list::iterator b (cid_cols->begin ()), i (b), + j (oid_cols->begin ()); i != cid_cols->end (); ++i, ++j) + { - string const& id (column_qname (*im, "id", "object_id")); + if (i != b) + dcond << " AND "; - dcond << da << '.' << column_qname (*id_member (*c)) << " = " << - t << '.' << id; + dcond << da << '.' << quote_id (j->name) << '=' << + t << '.' << quote_id (i->name); + } } } else { - t = table_qname (*c); - a = quote_id (column); + t = table_qname (c); + a = quote_id (alias); + + instance id_cols; + id_cols->traverse (*im); + + for (object_columns_list::iterator b (id_cols->begin ()), i (b), + j (id_cols_->begin ()); i != id_cols->end (); ++i, ++j) + { + + if (i != b) + cond << " AND "; - cond << a << '.' << column_qname (*im) << " = " << - table_ << "." << column_qname (id_); + cond << a << '.' << quote_id (i->name) << '=' << + table_ << '.' << quote_id (j->name); + } } } else if (query_) @@ -453,11 +546,25 @@ 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 (column); + t = table_qname (c); + a = quote_id (alias); - cond << a << '.' << column_qname (*id_member (*c)) << " = " << - table_ << "." << quote_id (column); + instance oid_cols (column_prefix_); + oid_cols->traverse (m); + + instance pid_cols; + pid_cols->traverse (*id_member (c)); + + for (object_columns_list::iterator b (pid_cols->begin ()), i (b), + j (oid_cols->begin ()); i != pid_cols->end (); ++i, ++j) + { + + if (i != b) + cond << " AND "; + + cond << a << '.' << quote_id (i->name) << '=' << + table_ << '.' << quote_id (j->name); + } } if (!t.empty ()) @@ -478,14 +585,13 @@ namespace relational joins_.back ().alias = da; joins_.back ().cond = dcond.str (); } - - return true; } private: bool query_; string table_; semantics::data_member& id_; + instance id_cols_; struct join { @@ -690,6 +796,207 @@ namespace relational string member_override_; }; + template + struct init_image_member_impl: init_image_member, + virtual member_base_impl + { + typedef init_image_member_impl base_impl; + + init_image_member_impl (base const& x) + : base (x), + member_database_type_id_ (base::type_override_, + base::fq_type_override_, + base::key_prefix_) + { + } + + typedef typename member_base_impl::member_info member_info; + + virtual void + set_null (member_info&) = 0; + + virtual bool + pre (member_info& mi) + { + // Ignore containers (they get their own table) and inverse + // object pointers (they are not present in this binding). + // + if (container (mi) || inverse (mi.m, key_prefix_)) + return false; + + if (!member_override_.empty ()) + member = member_override_; + else + { + // If we are generating standard init() and this member + // contains version, ignore it. + // + if (version (mi.m)) + return false; + + // If we don't send auto id in INSERT statement, ignore this + // member altogether (we never send auto id in UPDATE). + // + if (!insert_send_auto_id && id (mi.m) && auto_ (mi.m)) + return false; + + string const& name (mi.m.name ()); + member = "o." + name; + + os << "// " << name << endl + << "//" << endl; + + // If the whole class is readonly, then we will never be + // called with sk == statement_update. + // + if (!readonly (*context::top_object)) + { + semantics::class_* c; + + if (id (mi.m) || + readonly (mi.m) || + ((c = composite (mi.t)) && readonly (*c))) // Can't be id. + os << "if (sk == statement_insert)"; + } + } + + bool comp (composite (mi.t)); + + // If this is a wrapped composite value, then we need to "unwrap" + // it. For simple values this is taken care of by the value_traits + // specializations. + // + if (mi.wrapper != 0 && comp) + { + // Here we need the wrapper type, not the wrapped type. + // + member = "wrapper_traits< " + mi.fq_type (false) + " >::" + + "get_ref (" + member + ")"; + } + + if (mi.ptr != 0) + { + // When handling a pointer, mi.t is the id type of the referenced + // object. + // + semantics::type& pt (member_utype (mi.m, key_prefix_)); + + type = "obj_traits::id_type"; + + // Handle NULL pointers and extract the id. + // + os << "{" + << "typedef object_traits< " << class_fq_name (*mi.ptr) << + " > obj_traits;"; + + if (weak_pointer (pt)) + { + os << "typedef pointer_traits< " << mi.ptr_fq_type () << + " > wptr_traits;" + << "typedef pointer_traits< wptr_traits::" << + "strong_pointer_type > ptr_traits;" + << endl + << "wptr_traits::strong_pointer_type sp (" << + "wptr_traits::lock (" << member << "));"; + + member = "sp"; + } + else + os << "typedef pointer_traits< " << mi.ptr_fq_type () << + " > ptr_traits;" + << endl; + + os << "bool is_null (ptr_traits::null_ptr (" << member << "));" + << "if (!is_null)" + << "{" + << "const " << type << "& id (" << endl; + + if (lazy_pointer (pt)) + os << "ptr_traits::object_id< ptr_traits::element_type > (" << + member << ")"; + else + os << "obj_traits::id (ptr_traits::get_ref (" << member << "))"; + + os << ");" + << endl; + + member = "id"; + } + else if (comp) + { + type = mi.fq_type (); + + os << "{"; + } + else + { + type = mi.fq_type (); + + os << "{" + << "bool is_null;"; + } + + if (comp) + traits = "composite_value_traits< " + type + " >"; + else + { + db_type_id = member_database_type_id_->database_type_id (mi.m); + traits = string (db.string ()) + "::value_traits<\n " + + type + ",\n " + + db_type_id + " >"; + } + + return true; + } + + virtual void + post (member_info& mi) + { + if (mi.ptr != 0) + { + os << "}" + << "else" << endl; + + // @@ Composite value currently cannot be NULL. + // + if (!null (mi.m, key_prefix_) || composite (mi.t)) + os << "throw null_pointer ();"; + else + set_null (mi); + } + + os << "}"; + } + + virtual void + traverse_composite (member_info& mi) + { + bool grow (generate_grow && context::grow (mi.m, mi.t, key_prefix_)); + + if (grow) + os << "if ("; + + os << traits << "::init (" << endl + << "i." << mi.var << "value," << endl + << member << "," << endl + << "sk)"; + + if (grow) + os << ")" << endl + << "grew = true"; + + os << ";"; + } + + protected: + string type; + string db_type_id; + string member; + string traits; + + instance member_database_type_id_; + }; + struct init_image_base: traversal::class_, virtual context { typedef init_image_base base; @@ -755,6 +1062,171 @@ namespace relational string member_override_; }; + template + struct init_value_member_impl: init_value_member, + virtual member_base_impl + { + typedef init_value_member_impl base_impl; + + init_value_member_impl (base const& x) + : base (x), + member_database_type_id_ (base::type_override_, + base::fq_type_override_, + base::key_prefix_) + { + } + + typedef typename member_base_impl::member_info member_info; + + virtual void + get_null (member_info&) = 0; + + virtual bool + pre (member_info& mi) + { + if (container (mi)) + return false; + + if (!member_override_.empty ()) + member = member_override_; + else + { + string const& name (mi.m.name ()); + member = "o." + name; + + if (mi.cq) + { + string t (mi.ptr == 0 ? mi.fq_type (false) : mi.ptr_fq_type ()); + member = "const_cast< " + t + "& > (" + member + ")"; + } + + os << "// " << name << endl + << "//" << endl; + } + + bool comp (composite (mi.t)); + + // If this is a wrapped composite value, then we need to + // "unwrap" it. For simple values this is taken care of + // by the value_traits specializations. + // + if (mi.wrapper != 0 && comp) + { + // Here we need the wrapper type, not the wrapped type. + // + member = "wrapper_traits< " + mi.fq_type (false) + " >::" + + "set_ref (\n" + member + ")"; + } + + if (mi.ptr != 0) + { + type = "obj_traits::id_type"; + + // Handle NULL pointers and extract the id. + // + os << "{" + << "typedef object_traits< " << class_fq_name (*mi.ptr) << + " > obj_traits;" + << "typedef pointer_traits< " << mi.ptr_fq_type () << + " > ptr_traits;" + << endl; + + // @@ Composite value currently cannot be NULL. + // + if (!comp) + { + os << "if ("; + get_null (mi); + os << ")" << endl; + + if (!null (mi.m, key_prefix_) ) + os << "throw null_pointer ();"; + else + os << member << " = ptr_traits::pointer_type ();"; + + os << "else" + << "{"; + } + + os << type << " id;"; + + member = "id"; + } + else + type = mi.fq_type (); + + if (comp) + traits = "composite_value_traits< " + type + " >"; + else + { + db_type_id = member_database_type_id_->database_type_id (mi.m); + traits = string (db.string ()) + "::value_traits<\n " + + type + ",\n " + + db_type_id + " >"; + } + + return true; + } + + virtual void + post (member_info& mi) + { + if (mi.ptr != 0) + { + if (!member_override_.empty ()) + member = member_override_; + else + { + member = "o." + mi.m.name (); + + if (mi.cq) + member = "const_cast< " + mi.ptr_fq_type () + + "& > (" + member + ")"; + } + + // When handling a pointer, mi.t is the id type of the referenced + // object. + // + semantics::type& pt (member_utype (mi.m, key_prefix_)); + + if (lazy_pointer (pt)) + os << member << " = ptr_traits::pointer_type (*db, id);"; + else + os << "// If a compiler error points to the line below, then" << endl + << "// it most likely means that a pointer used in a member" << endl + << "// cannot be initialized from an object pointer." << endl + << "//" << endl + << member << " = ptr_traits::pointer_type (" << endl + << "db->load< obj_traits::object_type > (id));"; + + // @@ Composite value currently cannot be NULL. + // + if (!composite (mi.t)) + os << "}"; + + os << "}"; + } + } + + virtual void + traverse_composite (member_info& mi) + { + os << traits << "::init (" << endl + << member << "," << endl + << "i." << mi.var << "value," << endl + << "db);" + << endl; + } + + protected: + string type; + string db_type_id; + string traits; + string member; + + instance member_database_type_id_; + }; + struct init_value_base: traversal::class_, virtual context { typedef init_value_base base; @@ -930,7 +1402,10 @@ namespace relational // if (!abst) { + semantics::type& idt (container_idt (m)); + string table (table_qname (m, table_prefix_)); + instance id_cols; // select_all_statement // @@ -940,10 +1415,12 @@ namespace relational if (inverse) { semantics::class_* c (object_pointer (vt)); + semantics::data_member& inv_id (*id_member (*c)); - string inv_table; // Other table name. - string inv_id; // Other id column. - string inv_fid; // Other foreign id column (ref to us). + string inv_table; // Other table name. + instance inv_id_cols; // Other id column. + instance inv_fid_cols; // Other foreign id + // column (ref to us). statement_columns sc; if (container (*im)) @@ -956,23 +1433,42 @@ namespace relational // table_prefix tp (schema (c->scope ()), table_name (*c) + "_", 1); inv_table = table_qname (*im, tp); - inv_id = column_qname (*im, "id", "object_id"); - inv_fid = column_qname (*im, "value", "value"); - sc.push_back (statement_column ( - inv_table + "." + inv_id, *im, "id")); + inv_id_cols->traverse (*im, utype (inv_id), "id", "object_id", c); + inv_fid_cols->traverse (*im, idt, "value", "value"); + + for (object_columns_list::iterator i (inv_id_cols->begin ()); + i != inv_id_cols->end (); ++i) + { + // If this is a simple id, then pass the "id" key prefix. If + // it is a composite id, then the members have no prefix. + // + sc.push_back ( + statement_column ( + inv_table + "." + quote_id (i->name), + i->type, + *i->member, + inv_id_cols->size () == 1 ? "id" : "")); + } } else { // many(i)-to-one // - semantics::data_member& id (*id_member (*c)); - inv_table = table_qname (*c); - inv_id = column_qname (id); - inv_fid = column_qname (*im); - sc.push_back (statement_column (inv_table + "." + inv_id, id)); + inv_id_cols->traverse (inv_id); + inv_fid_cols->traverse (*im); + + for (object_columns_list::iterator i (inv_id_cols->begin ()); + i != inv_id_cols->end (); ++i) + { + sc.push_back ( + statement_column ( + inv_table + "." + quote_id (i->name), + i->type, + *i->member)); + } } process_statement_columns (sc, statement_select); @@ -987,12 +1483,20 @@ namespace relational } instance qp; - os << strlit (" FROM " + inv_table + - " WHERE " + inv_table + "." + inv_fid + "=" + - qp->next ()); + os << strlit (" FROM " + inv_table); + + for (object_columns_list::iterator b (inv_fid_cols->begin ()), + i (b); i != inv_fid_cols->end (); ++i) + { + os << endl + << strlit ((i == b ? " WHERE " : " AND ") + inv_table + "." + + quote_id (i->name) + "=" + qp->next ()); + } } else { + id_cols->traverse (m, idt, "id", "object_id"); + statement_columns sc; statement_kind sk (statement_select); // Imperfect forwarding. instance t (table, sk, sc); @@ -1002,22 +1506,13 @@ namespace relational case ck_ordered: { if (ordered) - { - string const& col (column_qname (m, "index", "index")); - t->column (m, "index", table, col); - } + t->traverse (m, *it, "index", "index"); break; } case ck_map: case ck_multimap: { - if (semantics::class_* ckt = composite_wrapper (*kt)) - t->traverse (m, *ckt, "key", "key"); - else - { - string const& col (column_qname (m, "key", "key")); - t->column (m, "key", table, col); - } + t->traverse (m, *kt, "key", "key"); break; } case ck_set: @@ -1027,13 +1522,7 @@ namespace relational } } - if (semantics::class_* cvt = composite_wrapper (vt)) - t->traverse (m, *cvt, "value", "value"); - else - { - string const& col (column_qname (m, "value", "value")); - t->column (m, "value", table, col); - } + t->traverse (m, vt, "value", "value"); process_statement_columns (sc, statement_select); @@ -1047,18 +1536,22 @@ namespace relational } instance qp; - string const& id_col (column_qname (m, "id", "object_id")); + os << strlit (" FROM " + table); - os << strlit (" FROM " + table + - " WHERE " + table + "." + id_col + "=" + - qp->next ()); + for (object_columns_list::iterator b (id_cols->begin ()), i (b); + i != id_cols->end (); ++i) + { + os << endl + << strlit ((i == b ? " WHERE " : " AND ") + table + "." + + quote_id (i->name) + "=" + qp->next ()); + } if (ordered) { string const& col (column_qname (m, "index", "index")); os << endl - << strlit (" ORDER BY " + table + "." + col) << endl; + << strlit (" ORDER BY " + table + "." + col); } } @@ -1076,29 +1569,23 @@ namespace relational else { statement_columns sc; - sc.push_back ( - statement_column ( - column_qname (m, "id", "object_id"), m, "id")); - statement_kind sk (statement_insert); // Imperfect forwarding. instance t (sk, sc); + t->traverse (m, idt, "id", "object_id"); + switch (ck) { case ck_ordered: { if (ordered) - t->column ( - m, "index", "", column_qname (m, "index", "index")); + t->traverse (m, *it, "index", "index"); break; } case ck_map: case ck_multimap: { - if (semantics::class_* ckt = composite_wrapper (*kt)) - t->traverse (m, *ckt, "key", "key"); - else - t->column (m, "key", "", column_qname (m, "key", "key")); + t->traverse (m, *kt, "key", "key"); break; } case ck_set: @@ -1108,10 +1595,7 @@ namespace relational } } - if (semantics::class_* cvt = composite_wrapper (vt)) - t->traverse (m, *cvt, "value", "value"); - else - t->column (m, "value", "", column_qname (m, "value", "value")); + t->traverse (m, vt, "value", "value"); process_statement_columns (sc, statement_insert); @@ -1151,9 +1635,17 @@ namespace relational { instance qp; - os << strlit ("DELETE FROM " + table) << endl - << strlit (" WHERE " + column_qname (m, "id", "object_id") + - "=" + qp->next ()) << ";" + os << 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; } } @@ -1462,10 +1954,10 @@ namespace relational { if (ordered) os << "init (index_type& j, value_type& v, " << - "const data_image_type& i, database& db)"; + "const data_image_type& i, database* db)"; else os << "init (value_type& v, const data_image_type& i, " << - "database& db)"; + "database* db)"; os << "{" << "ODB_POTENTIALLY_UNUSED (db);" @@ -1487,7 +1979,7 @@ namespace relational case ck_multimap: { os << "init (key_type& k, value_type& v, " << - "const data_image_type& i, database& db)" + "const data_image_type& i, database* db)" << "{" << "ODB_POTENTIALLY_UNUSED (db);" << endl @@ -1504,7 +1996,7 @@ namespace relational case ck_multiset: { os << "init (value_type& v, const data_image_type& i, " << - "database& db)" + "database* db)" << "{" << "ODB_POTENTIALLY_UNUSED (db);" << endl; @@ -1652,21 +2144,21 @@ namespace relational case ck_ordered: { os << "init (" << (ordered ? "i, " : "") << - "v, di, sts.connection ().database ());" + "v, di, &sts.connection ().database ());" << endl; break; } case ck_map: case ck_multimap: { - os << "init (k, v, di, sts.connection ().database ());" + os << "init (k, v, di, &sts.connection ().database ());" << endl; break; } case ck_set: case ck_multiset: { - os << "init (v, di, sts.connection ().database ());" + os << "init (v, di, &sts.connection ().database ());" << endl; break; } @@ -2130,26 +2622,30 @@ namespace relational } virtual void - traverse_simple (semantics::data_member& m) + traverse_pointer (semantics::data_member& m, semantics::class_& c) { if (!inverse (m)) - { - string p; + object_members_base::traverse_pointer (m, c); + } - if (version (m)) - p = "1"; - else if (id (m) && auto_ (m)) - p = qp_.auto_id (); - else - p = qp_.next (); + virtual void + traverse_simple (semantics::data_member& m) + { + string p; - if (!p.empty ()) - { - if (count_++ != 0) - params_ += ','; + if (version (m)) + p = "1"; + else if (context::id (m) && auto_ (m)) // Only simple id can be auto. + p = qp_.auto_id (); + else + p = qp_.next (); - params_ += p; - } + if (!p.empty ()) + { + if (count_++ != 0) + params_ += ','; + + params_ += p; } } @@ -2434,6 +2930,9 @@ namespace relational << 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;" @@ -2505,11 +3004,14 @@ namespace relational << "{" << "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++;" //@@ composite id + os << "n += " << column_count (c).id << ";" << endl; bind_version_member_->traverse (*optimistic); @@ -2549,7 +3051,7 @@ namespace relational // init (object, image) // os << "void " << traits << "::" << endl - << "init (object_type& o, const image_type& i, database& db)" + << "init (object_type& o, const image_type& i, database* db)" << "{" << "ODB_POTENTIALLY_UNUSED (o);" << "ODB_POTENTIALLY_UNUSED (i);" @@ -2571,8 +3073,10 @@ namespace relational << "{"; if (grow_id) - os << "bool grew (false);" - << endl; + os << "bool grew (false);"; + + if (composite_wrapper (utype (*id))) + os << db << "::statement_kind sk (" << db << "::statement_select);"; init_id_image_member_->traverse (*id); @@ -2677,7 +3181,8 @@ namespace relational if (id != 0) { - string const& id_col (column_qname (*id)); + instance id_cols; + id_cols->traverse (*id); // find_statement // @@ -2702,14 +3207,23 @@ namespace relational os << strlit (" FROM " + table) << endl; - bool f (false); + bool f (false); // @@ (im)perfect forwarding instance j (c, f); // @@ (im)perfect forwarding j->traverse (c); j->write (); instance qp; - os << strlit (" WHERE " + table + "." + id_col + "=" + - qp->next ()) << ";" + 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; } @@ -2739,13 +3253,22 @@ namespace relational os << strlit (c + (++i != e ? "," : "")) << endl; } - string where (" WHERE " + id_col + "=" + qp->next ()); + 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) - where += " AND " + column_qname (*optimistic) + "=" + - qp->next (); + os << endl + << strlit (" AND " + column_qname (*optimistic) + + "=" + qp->next ()); - os << strlit (where) << ";" + os << ";" << endl; } @@ -2754,8 +3277,18 @@ namespace relational { instance qp; os << "const char " << traits << "::erase_statement[] =" << endl - << strlit ("DELETE FROM " + table) << endl - << strlit (" WHERE " + id_col + "=" + qp->next ()) << ";" + << 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; } @@ -2763,13 +3296,21 @@ namespace relational { instance qp; - string where (" WHERE " + id_col + "=" + qp->next ()); - where += " AND " + column_qname (*optimistic) + "=" + qp->next (); - os << "const char " << traits << "::optimistic_erase_statement[] =" << endl - << strlit ("DELETE FROM " + table) << endl - << strlit (where) << ";" + << 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; } } @@ -2786,10 +3327,6 @@ namespace relational process_statement_columns (sc, statement_select); } - bool t (true); - instance oj (c, t); //@@ (im)perfect forwarding - oj->traverse (c); - os << "const char " << traits << "::query_statement[] =" << endl << strlit ("SELECT ") << endl; @@ -2801,7 +3338,15 @@ namespace relational } os << strlit (" FROM " + table) << endl; - oj->write (); + + if (id != 0) + { + bool t (true); //@@ (im)perfect forwarding + instance oj (c, t); //@@ (im)perfect forwarding + oj->traverse (c); + oj->write (); + } + os << strlit (" ") << ";" << endl; @@ -3202,7 +3747,7 @@ namespace relational << "if (l.locked ())" << "{" << "callback (db, obj, callback_event::pre_load);" - << "init (obj, sts.image (), db);"; + << "init (obj, sts.image (), &db);"; init_value_extra (); free_statement_result_delayed (); @@ -3248,7 +3793,7 @@ namespace relational << "reference_cache_traits< object_type >::insert (db, id, obj));" << endl << "callback (db, obj, callback_event::pre_load);" - << "init (obj, sts.image (), db);"; + << "init (obj, sts.image (), &db);"; init_value_extra (); free_statement_result_delayed (); @@ -3298,7 +3843,7 @@ namespace relational } os << "callback (db, obj, callback_event::pre_load);" - << "init (obj, sts.image (), db);"; + << "init (obj, sts.image (), &db);"; init_value_extra (); free_statement_result_delayed (); @@ -3766,7 +4311,7 @@ namespace relational // init (view, image) // os << "void " << traits << "::" << endl - << "init (view_type& o, const image_type& i, database& db)" + << "init (view_type& o, const image_type& i, database* db)" << "{" << "ODB_POTENTIALLY_UNUSED (o);" << "ODB_POTENTIALLY_UNUSED (i);" @@ -4080,7 +4625,7 @@ namespace relational if (im != 0) { // For now a direct member can only be directly in - // the object scope. When this changes, the inverse() + // the object scope. If this changes, the inverse() // function would have to return a member path instead // of just a single member. // @@ -4102,34 +4647,36 @@ namespace relational // 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. + // 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 // - l = ct; - l += '.'; - l += column_qname (*im, "value", "value"); - l += "="; - l += quote_id (lt); - l += '.'; - l += column_qname (*id_member (*e.vo->obj)); + 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 // - l = ct; - l += '.'; - l += column_qname (*im, "id", "object_id"); - l += "="; - l += quote_id (rt); - l += '.'; - l += column_qname (*id_member (*vo->obj)); + 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 @@ -4138,29 +4685,43 @@ namespace relational { // container.id = pointer.id // - l = ct; - l += '.'; - l += column_qname (m, "id", "object_id"); - l += "="; - l += quote_id (lt); - l += '.'; - l += column_qname (*id_member (*e.vo->obj)); + 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 // - l = ct; - l += '.'; - l += column_qname (m, "value", "value"); - l += "="; - l += quote_id (rt); - l += '.'; - l += column_qname (*id_member (*vo->obj)); + semantics::data_member& id (*id_member (*vo->obj)); + + c_cols->traverse (m, utype (id), "value", "value"); + o_cols->traverse (id); + ot = &rt; } } - os << "r += " << strlit (l) << ";"; + 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 "; @@ -4174,31 +4735,33 @@ namespace relational 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 // - l = ct; - l += '.'; - l += column_qname (*im, "id", "object_id"); - l += "="; - l += quote_id (rt); - l += '.'; - l += column_qname (*id_member (*vo->obj)); + 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 // - l = ct; - l += '.'; - l += column_qname (*im, "value", "value"); - l += "="; - l += quote_id (lt); - l += '.'; - l += column_qname (*id_member (*e.vo->obj)); + semantics::data_member& id (*id_member (*e.vo->obj)); + + c_cols->traverse (*im, utype (id), "value", "value"); + o_cols->traverse (id); + ot = < } } else @@ -4207,58 +4770,91 @@ namespace relational { // container.value = pointed-to.id // - l = ct; - l += '.'; - l += column_qname (m, "value", "value"); - l += "="; - l += quote_id (rt); - l += '.'; - l += column_qname (*id_member (*vo->obj)); + 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 // - l = ct; - l += '.'; - l += column_qname (m, "id", "object_id"); - l += "="; - l += quote_id (lt); - l += '.'; - l += column_qname (*id_member (*e.vo->obj)); + 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 = quote_id (lt); - l += '.'; - l += column_qname (*id_member (*e.vo->obj)); - l += " = "; - l += quote_id (rt); - l += '.'; - l += column_qname (*im); + l_cols->traverse (*id_member (*e.vo->obj)); + r_cols->traverse (*im); } else { // our.pointer = pointed-to.id // - l = quote_id (lt); + 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 += column_qname (e.member_path); - l += " = "; + l += quote_id (i->name); + l += '='; l += quote_id (rt); l += '.'; - l += column_qname (*id_member (*vo->obj)); + l += quote_id (j->name); + + os << "r += " << strlit (l) << ";"; } } - os << "r += " << strlit (l) << ";" - << endl; + os << endl; } // Generate the query condition. @@ -4499,7 +5095,7 @@ namespace relational // init (value, image) // os << "void " << traits << "::" << endl - << "init (value_type& o, const image_type& i, database& db)" + << "init (value_type& o, const image_type& i, database* db)" << "{" << "ODB_POTENTIALLY_UNUSED (o);" << "ODB_POTENTIALLY_UNUSED (i);" @@ -4588,8 +5184,12 @@ namespace relational << "#include " << endl << "#include " << endl << "#include " << endl - << "#include " << endl - << "#include " << endl + << "#include " << endl; + + if (options.generate_query ()) + os << "#include " << endl; + + os << "#include " << endl << "#include " << endl; if (options.generate_query ()) diff --git a/odb/relational/sqlite/common.cxx b/odb/relational/sqlite/common.cxx index 480b69a..7164ef3 100644 --- a/odb/relational/sqlite/common.cxx +++ b/odb/relational/sqlite/common.cxx @@ -16,91 +16,10 @@ namespace relational // member_base // - void member_base:: - traverse (semantics::data_member& m) + sql_type const& member_base:: + member_sql_type (semantics::data_member& m) { - if (transient (m)) - return; - - string var; - - if (!var_override_.empty ()) - var = var_override_; - else - { - string const& name (m.name ()); - var = name + (name[name.size () - 1] == '_' ? "" : "_"); - } - - bool cq (type_override_ != 0 ? false : const_type (m.type ())); - semantics::type& t (type_override_ != 0 ? *type_override_ : utype (m)); - - semantics::type* cont; - if (semantics::class_* c = composite_wrapper (t)) - { - // If t is a wrapper, pass the wrapped type. Also pass the - // original, wrapper type. - // - member_info mi (m, - *c, - (wrapper (t) ? &t : 0), - cq, - var, - fq_type_override_); - if (pre (mi)) - { - traverse_composite (mi); - post (mi); - } - } - // This cannot be a container if we have a type override. - // - else if (type_override_ == 0 && (cont = context::container (m))) - { - // The same unwrapping logic as for composite values. - // - member_info mi (m, - *cont, - (wrapper (t) ? &t : 0), - cq, - var, - fq_type_override_); - if (pre (mi)) - { - traverse_container (mi); - post (mi); - } - } - else - { - sql_type const& st (column_sql_type (m, key_prefix_)); - - if (semantics::class_* c = object_pointer (t)) - { - member_info mi (m, - utype (*id_member (*c)), - 0, - cq, - var, - fq_type_override_); - mi.st = &st; - if (pre (mi)) - { - traverse_object_pointer (mi); - post (mi); - } - } - else - { - member_info mi (m, t, 0, cq, var, fq_type_override_); - mi.st = &st; - if (pre (mi)) - { - traverse_simple (mi); - post (mi); - } - } - } + return parse_sql_type (column_type (m, key_prefix_), m); } void member_base:: @@ -185,10 +104,18 @@ namespace relational // member_database_type_id:: + member_database_type_id (base const& x) + : member_base::base (x), // virtual base + base (x) + { + } + + member_database_type_id:: member_database_type_id (semantics::type* type, - string const& fq_type, - string const& key_prefix) - : relational::member_base (type, fq_type, key_prefix) + string const& fq_type, + string const& key_prefix) + : member_base::base (type, fq_type, key_prefix), // virtual base + base (type, fq_type, key_prefix) { } @@ -230,6 +157,8 @@ namespace relational type_id_ = "sqlite::id_blob"; } + entry member_database_type_id_; + // // query_columns // diff --git a/odb/relational/sqlite/common.hxx b/odb/relational/sqlite/common.hxx index 7885a04..941e200 100644 --- a/odb/relational/sqlite/common.hxx +++ b/odb/relational/sqlite/common.hxx @@ -12,118 +12,18 @@ namespace relational { namespace sqlite { - struct member_base: virtual relational::member_base, context + struct member_base: virtual relational::member_base_impl, context { - member_base (base const& x): base (x) {} + member_base (base const& x): base (x), base_impl (x) {} // This c-tor is for the direct use inside the sqlite namespace. // If you do use this c-tor, you should also explicitly call - // relational::member_base. + // relational::member_base (aka base). // member_base () {} - virtual void - traverse (semantics::data_member& m); - - struct member_info - { - semantics::data_member& m; // Member. - semantics::type& t; // Cvr-unqualified member C++ type, note - // that m.type () may not be the same as t. - semantics::type* wrapper; // Wrapper type if member is a composite or - // container wrapper, also cvr-unqualified. - // In this case t is the wrapped type. - bool cq; // True if the original (wrapper) type - // is const-qualified. - sql_type const* st; // Member SQL type (only simple values). - string& var; // Member variable name with trailing '_'. - - // C++ type fq-name. - // - string - fq_type (bool unwrap = true) const - { - semantics::names* hint; - - if (wrapper != 0 && unwrap) - { - // Use the hint from the wrapper unless the wrapped type - // is qualified. - // - hint = wrapper->get ("wrapper-hint"); - utype (*context::wrapper (*wrapper), hint); - return t.fq_name (hint); - } - - // Use the original type from 'm' instead of 't' since the hint - // may be invalid for a different type. Plus, if a type is - // overriden, then the fq_type must be as well. - // - if (fq_type_.empty ()) - { - semantics::type& t (utype (m, hint)); - return t.fq_name (hint); - } - else - return fq_type_; - } - - string const& fq_type_; - - member_info (semantics::data_member& m_, - semantics::type& t_, - semantics::type* wrapper_, - bool cq_, - string& var_, - string const& fq_type) - : m (m_), - t (t_), - wrapper (wrapper_), - cq (cq_), - st (0), - var (var_), - fq_type_ (fq_type) - { - } - }; - - bool - container (member_info& mi) - { - // This cannot be a container if we have a type override. - // - return type_override_ == 0 && context::container (mi.m); - } - - // The false return value indicates that no further callbacks - // should be called for this member. - // - virtual bool - pre (member_info&) - { - return true; - } - - virtual void - post (member_info&) - { - } - - virtual void - traverse_composite (member_info&) - { - } - - virtual void - traverse_container (member_info&) - { - } - - virtual void - traverse_object_pointer (member_info& mi) - { - traverse_simple (mi); - } + virtual sql_type const& + member_sql_type (semantics::data_member&); virtual void traverse_simple (member_info&); @@ -182,12 +82,16 @@ namespace relational string type_; }; - struct member_database_type_id: member_base + struct member_database_type_id: relational::member_database_type_id, + member_base { + member_database_type_id (base const&); + member_database_type_id (semantics::type* type = 0, string const& fq_type = string (), string const& key_prefix = string ()); - string + + virtual string database_type_id (type&); virtual void diff --git a/odb/relational/sqlite/context.cxx b/odb/relational/sqlite/context.cxx index b04dcb9..46689bd 100644 --- a/odb/relational/sqlite/context.cxx +++ b/odb/relational/sqlite/context.cxx @@ -365,17 +365,20 @@ namespace relational } sql_type const& context:: - column_sql_type (semantics::data_member& m, string const& kp) + parse_sql_type (string const& t, semantics::data_member& m) { - string key (kp.empty () - ? string ("sqlite-column-sql-type") - : "sqlite-" + kp + "-column-sql-type"); + // If this proves to be too expensive, we can maintain a + // cache of parsed types. + // + data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t)); - if (!m.count (key)) + if (i != data_->sql_type_cache_.end ()) + return i->second; + else { try { - m.set (key, parse_sql_type (column_type (m, kp))); + return (data_->sql_type_cache_[t] = parse_sql_type (t)); } catch (invalid_sql_type const& e) { @@ -385,8 +388,6 @@ namespace relational throw operation_failed (); } } - - return m.get (key); } sql_type context:: diff --git a/odb/relational/sqlite/context.hxx b/odb/relational/sqlite/context.hxx index e9f755c..4123b0c 100644 --- a/odb/relational/sqlite/context.hxx +++ b/odb/relational/sqlite/context.hxx @@ -5,6 +5,8 @@ #ifndef ODB_RELATIONAL_SQLITE_CONTEXT_HXX #define ODB_RELATIONAL_SQLITE_CONTEXT_HXX +#include + #include namespace relational @@ -33,8 +35,7 @@ namespace relational { public: sql_type const& - column_sql_type (semantics::data_member&, - string const& key_prefix = string ()); + parse_sql_type (string const&, semantics::data_member&); public: struct invalid_sql_type @@ -87,6 +88,9 @@ namespace relational struct data: base_context::data { data (std::ostream& os): base_context::data (os) {} + + typedef std::map sql_type_cache; + sql_type_cache sql_type_cache_; }; data* data_; diff --git a/odb/relational/sqlite/model.cxx b/odb/relational/sqlite/model.cxx index 60522a3..41d5d6c 100644 --- a/odb/relational/sqlite/model.cxx +++ b/odb/relational/sqlite/model.cxx @@ -28,7 +28,7 @@ namespace relational { // Make sure the column is mapped to INTEGER. // - if (column_sql_type (m).type != sql_type::INTEGER) + if (parse_sql_type (column_type (), m).type != sql_type::INTEGER) { cerr << m.file () << ":" << m.line () << ":" << m.column () << ": error: column with default value specified as C++ " diff --git a/odb/relational/sqlite/source.cxx b/odb/relational/sqlite/source.cxx index 024694f..c170904 100644 --- a/odb/relational/sqlite/source.cxx +++ b/odb/relational/sqlite/source.cxx @@ -269,173 +269,21 @@ namespace relational // init image // - struct init_image_member: relational::init_image_member, member_base + struct init_image_member: relational::init_image_member_impl, + member_base { init_image_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_database_type_id_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - // Ignore containers (they get their own table) and inverse - // object pointers (they are not present in this binding). - // - if (container (mi) || inverse (mi.m, key_prefix_)) - return false; - - if (!member_override_.empty ()) - member = member_override_; - else - { - // If we are generating standard init() and this member - // contains version, ignore it. - // - if (version (mi.m)) - return false; - - string const& name (mi.m.name ()); - member = "o." + name; - - os << "// " << name << endl - << "//" << endl; - - // If the whole class is readonly, then we will never be - // called with sk == statement_update. - // - 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_insert)"; - } - } - - // If this is a wrapped composite value, then we need to - // "unwrap" it. For simple values this is taken care of - // by the value_traits specializations. - // - if (mi.wrapper != 0 && composite (mi.t)) - { - // Here we need the wrapper type, not the wrapped type. - // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "get_ref (" + member + ")"; - } - - if (composite (mi.t)) - { - os << "{"; - traits = "composite_value_traits< " + mi.fq_type () + " >"; - } - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (semantics::class_* c = object_pointer (mt)) - { - type = "obj_traits::id_type"; - db_type_id = member_database_type_id_.database_type_id (mi.m); - - // Handle NULL pointers and extract the id. - // - os << "{" - << "typedef object_traits< " << class_fq_name (*c) << - " > obj_traits;"; - - if (weak_pointer (mt)) - { - os << "typedef pointer_traits< " << mi.fq_type () << - " > wptr_traits;" - << "typedef pointer_traits< wptr_traits::" << - "strong_pointer_type > ptr_traits;" - << endl - << "wptr_traits::strong_pointer_type sp (" << - "wptr_traits::lock (" << member << "));"; - - member = "sp"; - } - else - os << "typedef pointer_traits< " << mi.fq_type () << - " > ptr_traits;" - << endl; - - os << "i." << mi.var << "null = ptr_traits::null_ptr (" << - member << ");" - << "if (!i." << mi.var << "null)" - << "{" - << "const " << type << "& id (" << endl; - - if (lazy_pointer (mt)) - os << "ptr_traits::object_id< ptr_traits::element_type > (" << - member << ")"; - else - os << "obj_traits::id (ptr_traits::get_ref (" << member << "))"; - - os << ");" - << endl; - - member = "id"; - } - else - { - type = mi.fq_type (); - db_type_id = member_database_type_id_.database_type_id (mi.m); - - os << "{"; - } - - traits = "sqlite::value_traits<\n " - + type + ",\n " - + db_type_id + " >"; - } - - return true; - } - - virtual void - post (member_info& mi) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) { - if (composite (mi.t)) - os << "}"; - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - if (object_pointer (member_utype (mi.m, key_prefix_))) - { - os << "}"; - - if (!null (mi.m, key_prefix_)) - os << "else" << endl - << "throw null_pointer ();"; - } - - os << "}"; - } } virtual void - traverse_composite (member_info& mi) + set_null (member_info& mi) { - os << "if (" << traits << "::init (" << endl - << "i." << mi.var << "value," << endl - << member << "," << endl - << "sk))" << endl - << "grew = true;"; + os << "i." << mi.var << "null = true;"; } virtual void @@ -443,8 +291,9 @@ namespace relational { os << traits << "::set_image (" << endl << "i." << mi.var << "value," << endl - << "i." << mi.var << "null," << endl - << member << ");"; + << "is_null," << endl + << member << ");" + << "i." << mi.var << "null = is_null;"; } virtual void @@ -452,8 +301,9 @@ namespace relational { os << traits << "::set_image (" << endl << "i." << mi.var << "value," << endl - << "i." << mi.var << "null," << endl - << member << ");"; + << "is_null," << endl + << member << ");" + << "i." << mi.var << "null = is_null;"; } virtual void @@ -463,18 +313,11 @@ namespace relational << traits << "::set_image (" << endl << "i." << mi.var << "value," << endl << "i." << mi.var << "size," << endl - << "i." << mi.var << "null," << endl + << "is_null," << endl << member << ");" + << "i." << mi.var << "null = is_null;" << "grew = grew || (cap != i." << mi.var << "value.capacity ());"; } - - private: - string type; - string db_type_id; - string member; - string traits; - - member_database_type_id member_database_type_id_; }; entry init_image_member_; @@ -482,147 +325,21 @@ namespace relational // init value // - struct init_value_member: relational::init_value_member, member_base + struct init_value_member: relational::init_value_member_impl, + member_base { init_value_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_database_type_id_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - if (!member_override_.empty ()) - member = member_override_; - else - { - string const& name (mi.m.name ()); - member = "o." + name; - - if (mi.cq) - member = "const_cast< " + mi.fq_type (false) + "& > (" + - member + ")"; - - os << "// " << name << endl - << "//" << endl; - } - - // If this is a wrapped composite value, then we need to - // "unwrap" it. For simple values this is taken care of - // by the value_traits specializations. - // - if (mi.wrapper != 0 && composite (mi.t)) - { - // Here we need the wrapper type, not the wrapped type. - // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "set_ref (\n" + member + ")"; - } - - if (composite (mi.t)) - traits = "composite_value_traits< " + mi.fq_type () + " >"; - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (semantics::class_* c = object_pointer (mt)) - { - type = "obj_traits::id_type"; - db_type_id = member_database_type_id_.database_type_id (mi.m); - - // Handle NULL pointers and extract the id. - // - os << "{" - << "typedef object_traits< " << class_fq_name (*c) << - " > obj_traits;" - << "typedef pointer_traits< " << mi.fq_type () << - " > ptr_traits;" - << endl - << "if (i." << mi.var << "null)" << endl; - - if (null (mi.m, key_prefix_)) - os << member << " = ptr_traits::pointer_type ();"; - else - os << "throw null_pointer ();"; - - os << "else" - << "{" - << type << " id;"; - - member = "id"; - } - else - { - type = mi.fq_type (); - db_type_id = member_database_type_id_.database_type_id (mi.m); - } - - traits = "sqlite::value_traits<\n " - + type + ",\n " - + db_type_id + " >"; - } - - return true; - } - - virtual void - post (member_info& mi) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) { - if (composite (mi.t)) - return; - - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (object_pointer (mt)) - { - if (!member_override_.empty ()) - member = member_override_; - else - { - member = "o." + mi.m.name (); - - if (mi.cq) - member = "const_cast< " + mi.fq_type (false) + "& > (" + - member + ")"; - } - - if (lazy_pointer (mt)) - os << member << " = ptr_traits::pointer_type (db, id);"; - else - os << "// If a compiler error points to the line below, then" << endl - << "// it most likely means that a pointer used in a member" << endl - << "// cannot be initialized from an object pointer." << endl - << "//" << endl - << member << " = ptr_traits::pointer_type (" << endl - << "db.load< obj_traits::object_type > (id));"; - - os << "}" - << "}"; - } } virtual void - traverse_composite (member_info& mi) + get_null (member_info& mi) { - os << traits << "::init (" << endl - << member << "," << endl - << "i." << mi.var << "value," << endl - << "db);" - << endl; + os << "i." << mi.var << "null"; } virtual void @@ -655,14 +372,6 @@ namespace relational << "i." << mi.var << "null);" << endl; } - - private: - string type; - string db_type_id; - string traits; - string member; - - member_database_type_id member_database_type_id_; }; entry init_value_member_; @@ -680,7 +389,6 @@ namespace relational }; entry container_traits_; - struct class_: relational::class_, context { class_ (base const& x): base (x) {} diff --git a/odb/validator.cxx b/odb/validator.cxx index b8f7c91..c051487 100644 --- a/odb/validator.cxx +++ b/odb/validator.cxx @@ -42,7 +42,7 @@ namespace // Pass 1. // - struct data_member: traversal::data_member + struct data_member: traversal::data_member, context { data_member (bool& valid) : valid_ (valid) @@ -52,45 +52,42 @@ namespace virtual void traverse (type& m) { - if (context::transient (m)) + if (transient (m)) return; count_++; semantics::names* hint; - semantics::type& t (context::utype (m, hint)); + semantics::type& t (utype (m, hint)); if (t.fq_anonymous (hint)) { - cerr << m.file () << ":" << m.line () << ":" << m.column () << ":" - << " error: unnamed type in data member declaration" << endl; + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: unnamed type in data member declaration" << endl; - cerr << m.file () << ":" << m.line () << ":" << m.column () << ":" - << " info: use 'typedef' to name this type" << endl; + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " info: use 'typedef' to name this type" << endl; valid_ = false; } // Make sure id or inverse member is not marked readonly since we - // depend on these three sets not having overlaps. Once we support - // composite ids, we will also need to make sure there are no - // nested readonly members (probably move it to pass 2 and use - // column_count()). + // depend on these three sets not having overlaps. // - if (m.count ("readonly")) + if (readonly (m)) { - if (m.count ("id")) + if (id (m)) { - cerr << m.file () << ":" << m.line () << ":" << m.column () << ":" - << " error: object id should not be declared readonly" << endl; + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: object id should not be declared readonly" << endl; valid_ = false; } - if (m.count ("inverse")) + if (inverse (m)) { - cerr << m.file () << ":" << m.line () << ":" << m.column () << ":" - << " error: inverse object pointer should not be declared " - << "readonly" << endl; + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: inverse object pointer should not be declared " + << "readonly" << endl; valid_ = false; } @@ -108,9 +105,9 @@ namespace // Find special members (id, version). // - struct special_members: traversal::class_ + struct special_members: traversal::class_, context { - special_members (class_kind kind, + special_members (class_kind_type kind, bool& valid, semantics::data_member*& id, semantics::data_member*& optimistic) @@ -131,7 +128,7 @@ namespace { case class_object: { - if (!context::object (c)) + if (!object (c)) return; break; } @@ -141,7 +138,7 @@ namespace } case class_composite: { - if (!context::composite (c)) + if (!composite (c)) return; break; } @@ -161,7 +158,7 @@ namespace } private: - struct member: traversal::data_member + struct member: traversal::data_member, context { member (bool& valid, semantics::data_member*& id, @@ -173,17 +170,15 @@ namespace virtual void traverse (semantics::data_member& m) { - if (m.count ("id")) + if (id (m)) { if (id_ != 0) { - cerr << m.file () << ":" << m.line () << ":" << m.column () << ":" - << " error: multiple object id members" << endl; + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: multiple object id members" << endl; - semantics::data_member& i (*id_); - - cerr << i.file () << ":" << i.line () << ":" << i.column () - << ": info: previous id member is declared here" << endl; + os << id_->file () << ":" << id_->line () << ":" << id_->column () + << ": info: previous id member is declared here" << endl; valid_ = false; } @@ -191,17 +186,17 @@ namespace id_ = &m; } - if (m.count ("version")) + if (version (m)) { if (optimistic_ != 0) { - cerr << m.file () << ":" << m.line () << ":" << m.column () << ":" - << " error: multiple version members" << endl; + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: multiple version members" << endl; semantics::data_member& o (*optimistic_); - cerr << o.file () << ":" << o.line () << ":" << o.column () - << ": info: previous version member is declared here" << endl; + os << o.file () << ":" << o.line () << ":" << o.column () + << ": info: previous version member is declared here" << endl; valid_ = false; } @@ -215,7 +210,7 @@ namespace semantics::data_member*& optimistic_; }; - class_kind kind_; + class_kind_type kind_; member member_; traversal::names names_; traversal::inherits inherits_; @@ -223,7 +218,7 @@ namespace // // - struct value_type: traversal::type + struct value_type: traversal::type, context { value_type (bool& valid): valid_ (valid) {} @@ -241,17 +236,10 @@ namespace // // - struct class1: traversal::class_ + struct class1: traversal::class_, context { - class1 (bool& valid, - options const& ops, - semantics::unit& unit, - value_type& vt) - : valid_ (valid), - options_ (ops), - unit_ (unit), - vt_ (vt), - member_ (valid) + class1 (bool& valid, value_type& vt) + : valid_ (valid), vt_ (vt), member_ (valid) { *this >> names_ >> member_; } @@ -259,13 +247,13 @@ namespace virtual void traverse (type& c) { - if (context::object (c)) + if (object (c)) traverse_object (c); - else if (context::view (c)) + else if (view (c)) traverse_view (c); else { - if (context::composite (c)) + if (composite (c)) traverse_composite (c); vt_.dispatch (c); @@ -286,10 +274,10 @@ namespace if (decl == error_mark_node || TREE_CODE (decl) != BASELINK) { - cerr << c.file () << ":" << c.line () << ":" << c.column () << ": " - << "error: unable to resolve member function '" << name << "' " - << "specified with '#pragma db callback' for class '" - << context::class_name (c) << "'" << endl; + 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; } @@ -324,25 +312,25 @@ namespace { type& b (i->base ()); - if (context::object (b)) + if (object (b)) base = true; - else if (context::view (b) || context::composite (b)) + else if (view (b) || composite (b)) { // @@ Should we use hint here? // - string name (context::class_fq_name (b)); + string name (class_fq_name (b)); - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: base class '" << name << "' is a view or value type" - << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: base class '" << name << "' is a view or value type" + << endl; - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " info: object types cannot derive from view or value " - << "types" - << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " info: object types cannot derive from view or value " + << "types" + << endl; - cerr << b.file () << ":" << b.line () << ":" << b.column () << ":" - << " info: class '" << name << "' is defined here" << endl; + os << b.file () << ":" << b.line () << ":" << b.column () << ":" + << " info: class '" << name << "' is defined here" << endl; valid_ = false; } @@ -359,21 +347,21 @@ 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 abstract or explicitly + // marked as such. // - if (!(c.count ("id") || context::abstract (c))) + if (!(c.count ("id") || abstract (c))) { - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: no data member designated as an object id" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: no data member designated as an object id" << endl; - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " info: use '#pragma db id' to specify an object id member" - << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " info: use '#pragma db id' to specify an object id member" + << endl; - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " info: or explicitly declare that this persistent class " - << "has no object id" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " info: or explicitly declare that this persistent class " + << "has no object id" << endl; valid_ = false; } @@ -387,9 +375,9 @@ namespace // if (id->count ("default")) { - cerr << id->file () << ":" << id->line () << ":" << id->column () - << ": error: object id member cannot have default value" - << endl; + os << id->file () << ":" << id->line () << ":" << id->column () + << ": error: object id member cannot have default value" + << endl; valid_ = false; } @@ -399,8 +387,8 @@ namespace // if (id->count ("null")) { - cerr << id->file () << ":" << id->line () << ":" << id->column () - << ": error: object id member cannot be null" << endl; + os << id->file () << ":" << id->line () << ":" << id->column () + << ": error: object id member cannot be null" << endl; valid_ = false; } @@ -416,13 +404,13 @@ namespace // if (&optimistic->scope () == &c && !c.count ("optimistic")) { - cerr << m.file () << ":" << m.line () << ":" << m.column () << ":" - << " error: version data member in a class not declared " - << "optimistic" << endl; + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: version data member in a class not declared " + << "optimistic" << endl; - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " info: use '#pragma db optimistic' to declare this " - << "class optimistic" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " info: use '#pragma db optimistic' to declare this " + << "class optimistic" << endl; valid_ = false; } @@ -431,8 +419,8 @@ namespace // if (id == 0) { - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: optimistic class without an object id" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: optimistic class without an object id" << endl; valid_ = false; } @@ -442,34 +430,33 @@ namespace // if (id != 0 && &id->scope () != &optimistic->scope ()) { - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: object id and version members are in different " - << "classes" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: object id and version members are in different " + << "classes" << endl; - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " info: object id and version members must be in the same " - << "class" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " info: object id and version members must be in the same " + << "class" << endl; - cerr << id->file () << ":" << id->line () << ":" << id->column () - << ": info: object id member is declared here" << endl; + os << id->file () << ":" << id->line () << ":" << id->column () + << ": info: object id member is declared here" << endl; - cerr << m.file () << ":" << m.line () << ":" << m.column () << ":" - << " error: version member is declared here" << endl; + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: version member is declared here" << endl; valid_ = false; } // Make sure this class is not readonly. // - if (c.count ("readonly")) + if (readonly (c)) { - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: optimistic class cannot be readonly" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: optimistic class cannot be readonly" << endl; valid_ = false; } - // This takes care of also marking derived classes as optimistic. // c.set ("optimistic-member", optimistic); @@ -481,12 +468,12 @@ namespace // if (c.count ("optimistic")) { - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: optimistic class without a version member" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: optimistic class without a version member" << endl; - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " info: use '#pragma db version' to declare on of the " - << "data members as a version" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " info: use '#pragma db version' to declare on of the " + << "data members as a version" << endl; valid_ = false; } @@ -499,8 +486,8 @@ namespace if (member_.count_ == 0 && !base) { - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: no persistent data members in the class" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: no persistent data members in the class" << endl; valid_ = false; } @@ -511,16 +498,16 @@ namespace { // Views require query support. // - if (!options_.generate_query ()) + if (!options.generate_query ()) { - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: query support is required when using views" - << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: query support is required when using views" + << endl; - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " info: use the --generate-query option to enable query " - << "support" - << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " info: use the --generate-query option to enable query " + << "support" + << endl; valid_ = false; } @@ -533,26 +520,24 @@ namespace { type& b (i->base ()); - if (context::object (b) || - context::view (b) || - context::composite (b)) + if (object (b) || view (b) || composite (b)) { // @@ Should we use hint here? // - string name (context::class_fq_name (b)); + string name (class_fq_name (b)); - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: base class '" << name << "' is an object, " - << "view, or value type" - << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: base class '" << name << "' is an object, " + << "view, or value type" + << endl; - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " info: view types cannot derive from view, object or " - << "value types" - << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " info: view types cannot derive from view, object or " + << "value types" + << endl; - cerr << b.file () << ":" << b.line () << ":" << b.column () << ":" - << " info: class '" << name << "' is defined here" << endl; + os << b.file () << ":" << b.line () << ":" << b.column () << ":" + << " info: class '" << name << "' is defined here" << endl; valid_ = false; } @@ -569,9 +554,9 @@ namespace if (id != 0) { - cerr << id->file () << ":" << id->line () << ":" << id->column () - << ": error: view type data member cannot be designated as an " - << "object id" << endl; + os << id->file () << ":" << id->line () << ":" << id->column () + << ": error: view type data member cannot be designated as an " + << "object id" << endl; valid_ = false; } @@ -580,9 +565,9 @@ namespace { semantics::data_member& o (*optimistic); - cerr << o.file () << ":" << o.line () << ":" << o.column () - << ": error: view type data member cannot be designated as a " - << "version" << endl; + os << o.file () << ":" << o.line () << ":" << o.column () + << ": error: view type data member cannot be designated as a " + << "version" << endl; valid_ = false; } @@ -594,8 +579,8 @@ namespace if (member_.count_ == 0) { - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: no persistent data members in the class" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: no persistent data members in the class" << endl; valid_ = false; } @@ -612,25 +597,25 @@ namespace { type& b (i->base ()); - if (context::composite (b)) + if (composite (b)) base = true; - else if (context::object (b) || context::view (b)) + else if (object (b) || view (b)) { // @@ Should we use hint here? // - string name (context::class_fq_name (b)); + string name (class_fq_name (b)); - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: base class '" << name << "' is a view or object " - << "type" - << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: base class '" << name << "' is a view or object " + << "type" + << endl; - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " info: composite value types cannot derive from object " - << "or view types" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " info: composite value types cannot derive from object " + << "or view types" << endl; - cerr << b.file () << ":" << b.line () << ":" << b.column () << ":" - << " info: class '" << name << "' is defined here" << endl; + os << b.file () << ":" << b.line () << ":" << b.column () << ":" + << " info: class '" << name << "' is defined here" << endl; valid_ = false; } @@ -647,9 +632,9 @@ namespace if (id != 0) { - cerr << id->file () << ":" << id->line () << ":" << id->column () - << ": error: value type data member cannot be designated as an " - << "object id" << endl; + os << id->file () << ":" << id->line () << ":" << id->column () + << ": error: value type data member cannot be designated as an " + << "object id" << endl; valid_ = false; } @@ -658,9 +643,9 @@ namespace { semantics::data_member& o (*optimistic); - cerr << o.file () << ":" << o.line () << ":" << o.column () - << ": error: value type data member cannot be designated as a " - << "version" << endl; + os << o.file () << ":" << o.line () << ":" << o.column () + << ": error: value type data member cannot be designated as a " + << "version" << endl; valid_ = false; } @@ -672,16 +657,14 @@ namespace if (member_.count_ == 0 && !base) { - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: no persistent data members in the class" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: no persistent data members in the class" << endl; valid_ = false; } } bool& valid_; - options const& options_; - semantics::unit& unit_; value_type& vt_; data_member member_; @@ -720,15 +703,80 @@ namespace } virtual void + traverse_pointer (semantics::data_member& m, semantics::class_&) + { + if (inverse (m)) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: inverse object pointer member '" << member_prefix_ + << m.name () << "' in an object without an object id" << endl; + + valid_ = false; + } + } + + virtual void + traverse_container (semantics::data_member& m, semantics::type&) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: container member '" << member_prefix_ << m.name () + << "' in an object without an object id" << endl; + + valid_ = false; + } + + virtual void + traverse_composite (semantics::data_member* m, semantics::class_& c) + { + semantics::data_member* old_dm (dm_); + + if (dm_ == 0) + dm_ = m; + + object_members_base::traverse_composite (m, c); + + dm_ = old_dm; + } + + private: + bool& valid_; + semantics::data_member* dm_; // Direct object data member. + }; + + struct composite_id_members: object_members_base + { + composite_id_members (bool& valid) + : object_members_base (false, false, true), valid_ (valid), dm_ (0) + { + } + + virtual void + traverse_pointer (semantics::data_member& m, semantics::class_&) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: object pointer member '" << member_prefix_ << m.name () + << "' in a composite value type that is used as an object id" << endl; + + valid_ = false; + } + + virtual void traverse_simple (semantics::data_member& m) { - if (m.count ("inverse")) + if (readonly (member_path_, member_scope_)) { semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - cerr << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: inverse object pointer member '" << member_prefix_ - << m.name () << "' in an object without an object id" << endl; + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: readonly member '" << member_prefix_ << m.name () + << "' in a composite value type that is used as an object id" + << endl; valid_ = false; } @@ -739,9 +787,9 @@ namespace { semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - cerr << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: container member '" << member_prefix_ << m.name () - << "' in an object without an object id" << endl; + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: container member '" << member_prefix_ << m.name () + << "' in a composite value type that is used as an object id" << endl; valid_ = false; } @@ -761,7 +809,7 @@ namespace private: bool& valid_; - semantics::data_member* dm_; // Direct view data member. + semantics::data_member* dm_; // Direct composite member. }; struct view_members: object_members_base @@ -774,16 +822,16 @@ namespace virtual void traverse_simple (semantics::data_member& m) { - if (context::object_pointer (utype (m))) + if (object_pointer (utype (m))) { semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - cerr << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: view data member '" << member_prefix_ << m.name () - << "' is an object pointer" << endl; + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: view data member '" << member_prefix_ << m.name () + << "' is an object pointer" << endl; - cerr << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << ": info: views cannot contain object pointers" << endl; + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << ": info: views cannot contain object pointers" << endl; valid_ = false; } @@ -794,12 +842,12 @@ namespace { semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - cerr << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: view data member '" << member_prefix_ << m.name () - << "' is a container" << endl; + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: view data member '" << member_prefix_ << m.name () + << "' is a container" << endl; - cerr << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << ": info: views cannot contain containers" << endl; + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << ": info: views cannot contain containers" << endl; valid_ = false; } @@ -824,36 +872,175 @@ namespace // // - struct class2: traversal::class_ + struct class2: traversal::class_, context { - class2 (bool& valid, options const& ops, semantics::unit& unit) + class2 (bool& valid) : valid_ (valid), - options_ (ops), - unit_ (unit), object_no_id_members_ (valid), + composite_id_members_ (valid), view_members_ (valid) { + // Find the has_lt_operator function template.. + // + has_lt_operator_ = 0; + + tree odb ( + lookup_qualified_name ( + global_namespace, get_identifier ("odb"), false, false)); + + if (odb != error_mark_node) + { + tree compiler ( + lookup_qualified_name ( + odb, get_identifier ("compiler"), false, false)); + + if (compiler != error_mark_node) + { + has_lt_operator_ = lookup_qualified_name ( + compiler, get_identifier ("has_lt_operator"), false, false); + + if (has_lt_operator_ != error_mark_node) + has_lt_operator_ = OVL_CURRENT (has_lt_operator_); + else + { + os << unit.file () << ": error: unable to resolve has_lt_operator " + << "function template inside odb::compiler" << endl; + has_lt_operator_ = 0; + } + } + else + os << unit.file () << ": error: unable to resolve compiler " + << "namespace inside odb" << endl; + } + else + os << unit.file () << ": error: unable to resolve odb namespace" + << endl; + + if (has_lt_operator_ == 0) + valid_ = false; } virtual void traverse (type& c) { - if (context::object (c)) + if (object (c)) traverse_object (c); - else if (context::view (c)) + else if (view (c)) traverse_view (c); - else if (context::composite (c)) + else if (composite (c)) traverse_composite (c); } virtual void traverse_object (type& c) { - if (context::id_member (c) == 0 && !context::abstract (c)) + semantics::data_member* id (id_member (c)); + + if (id != 0) { - // Make sure we don't have any containers or inverse pointers. - // - object_no_id_members_.traverse (c); + if (semantics::class_* c = composite_wrapper (utype (*id))) + { + // Composite id cannot be auto. + // + if (auto_ (*id)) + { + os << id->file () << ":" << id->line () << ":" << id->column () + << ": error: composite id cannot be automatically assigned" + << endl; + + valid_ = false; + } + + // Make sure we don't have any containers or pointers in this + // composite value type. + // + if (valid_) + { + composite_id_members_.traverse (*c); + + if (!valid_) + os << id->file () << ":" << id->line () << ":" << id->column () + << ": info: composite id is defined here" << endl; + } + + // Check that the composite value type is default-constructible. + // + if (!c->default_ctor ()) + { + os << c->file () << ":" << c->line () << ":" << c->column () + << ": error: composite value type that is used as object id " + << "is not default-constructible" << endl; + + os << c->file () << ":" << c->line () << ":" << c->column () + << ": info: provide default constructor for this value type" + << endl; + + os << id->file () << ":" << id->line () << ":" << id->column () + << ": info: composite id is defined here" << endl; + + valid_ = false; + } + + // Check that composite values can be compared (used in session). + // + if (has_lt_operator_ != 0) + { + tree args (make_tree_vec (1)); + TREE_VEC_ELT (args, 0) = c->tree_node (); + + tree inst ( + instantiate_template ( + has_lt_operator_, args, tf_none)); + + bool v (inst != error_mark_node); + + if (v && + DECL_TEMPLATE_INSTANTIATION (inst) && + !DECL_TEMPLATE_INSTANTIATED (inst)) + { + // Instantiate this function template to see if the value type + // provides operator<. Unfortunately, GCC instantiate_decl() + // does not provide any control over the diagnostics it issues + // in case of an error. To work around this, we are going to + // temporarily redirect diagnostics to /dev/null, which is + // where asm_out_file points to (see plugin.cxx). + // + int ec (errorcount); + FILE* s (global_dc->printer->buffer->stream); + global_dc->printer->buffer->stream = asm_out_file; + + instantiate_decl (inst, false, false); + + global_dc->printer->buffer->stream = s; + v = (ec == errorcount); + } + + if (!v) + { + os << c->file () << ":" << c->line () << ":" << c->column () + << ": error: composite value type that is used as object id " + << "does not support the less than (<) comparison" + << endl; + + os << c->file () << ":" << c->line () << ":" << c->column () + << ": info: provide operator< for this value type" << endl; + + os << id->file () << ":" << id->line () << ":" << id->column () + << ": info: composite id is defined here" << endl; + + valid_ = false; + } + } + } + } + else + { + if (!abstract (c)) + { + // Make sure we don't have any containers or inverse pointers. + // + object_no_id_members_.traverse (c); + } } } @@ -871,10 +1058,10 @@ namespace } bool& valid_; - options const& options_; - semantics::unit& unit_; + tree has_lt_operator_; object_no_id_members object_no_id_members_; + composite_id_members composite_id_members_; view_members view_members_; }; } @@ -897,7 +1084,7 @@ validate (options const& ops, typedefs1 unit_typedefs (unit_declares); traversal::namespace_ ns; value_type vt (valid); - class1 c (valid, ops, u, vt); + class1 c (valid, vt); unit >> unit_defines >> ns; unit_defines >> c; @@ -921,7 +1108,7 @@ validate (options const& ops, traversal::defines unit_defines; typedefs unit_typedefs (true); traversal::namespace_ ns; - class2 c (valid, ops, u); + class2 c (valid); unit >> unit_defines >> ns; unit_defines >> c; -- cgit v1.1