From 2fe7a723c0b98d23531a76d9c1a451e3a57bf0c5 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 17 Nov 2010 18:05:06 +0200 Subject: Add support for unidirectional object relationships New test: common/relationship. --- odb/context.cxx | 64 +++++++++- odb/context.hxx | 59 +++++++-- odb/mysql/common.cxx | 227 +++++++++++++++++++---------------- odb/mysql/common.hxx | 17 ++- odb/mysql/context.cxx | 41 ++++--- odb/mysql/context.hxx | 3 +- odb/mysql/header.cxx | 13 +- odb/mysql/schema.cxx | 24 +++- odb/mysql/source.cxx | 315 ++++++++++++++++++++++++++++++++----------------- odb/type-processor.cxx | 200 +++++++++++++++++++++++++------ 10 files changed, 664 insertions(+), 299 deletions(-) diff --git a/odb/context.cxx b/odb/context.cxx index 552bc28..285d610 100644 --- a/odb/context.cxx +++ b/odb/context.cxx @@ -168,7 +168,8 @@ column_type (semantics::data_member& m, string const& kp) const string context::data:: column_type_impl (semantics::type& t, string const& type, - semantics::context* ctx) const + semantics::context& ctx, + column_type_flags f) const { if (!type.empty ()) return type; @@ -180,7 +181,14 @@ column_type_impl (semantics::type& t, type_map_type::const_iterator i (type_map_.find (name)); if (i != type_map_.end ()) - return ctx != 0 && ctx->count ("id") ? i->second.id_type : i->second.type; + { + string r (ctx.count ("id") ? i->second.id_type : i->second.type); + + if ((f & ctf_default_null) == 0) + r += " NOT NULL"; + + return r; + } return string (); } @@ -387,6 +395,58 @@ id_member (semantics::class_& c) return *c.get ("id-member"); } +namespace +{ + struct has_a_impl: object_members_base + { + has_a_impl (unsigned short flags) + : r_ (false), flags_ (flags) + { + } + + bool + result () const + { + return r_; + } + + virtual void + simple (semantics::data_member& m) + { + r_ = r_ || context::is_a (m, flags_); + } + + private: + bool r_; + unsigned short flags_; + }; +} + +bool context:: +is_a (semantics::data_member& m, + unsigned short f, + semantics::type&, + string const& kp) +{ + bool r (false); + + if (f & eager_pointer) + { + r = r || object_pointer (m, kp); + } + + return r; +} + +bool context:: +has_a (semantics::type& t, unsigned short flags) +{ + has_a_impl impl (flags); + impl.dispatch (t); + return impl.result (); +} + + // namespace // diff --git a/odb/context.hxx b/odb/context.hxx index 3fad543..2703380 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -74,6 +74,16 @@ public: return t.count ("container-kind"); } + static semantics::class_* + object_pointer (semantics::data_member& m, string const& key_prefix) + { + using semantics::class_; + + return key_prefix.empty () + ? m.get ("object-pointer", 0) + : m.get (key_prefix + "-object-pointer", 0); + } + // Database names and types. // public: @@ -103,17 +113,6 @@ public: column_type (semantics::data_member&, string const& key_prefix = string ()) const; - // Return empty string if there is no mapping. The second argument - // is the custom type or empty string if it is not specified. - // - string - column_type_impl (semantics::type& t, - string const& type, - semantics::context* ctx) const - { - return data_->column_type_impl (t, type, ctx); - } - // Cleaned-up member name that can be used for database names. // string @@ -171,6 +170,26 @@ public: return *c.get ("tree-key-type"); } + // The 'is a' and 'has a' tests. The has_a test currently does not + // cross the container boundaries. + // + static unsigned short const eager_pointer = 0x01; + + static bool + is_a (semantics::data_member& m, unsigned short flags) + { + return is_a (m, flags, m.type (), ""); + } + + static bool + is_a (semantics::data_member&, + unsigned short flags, + semantics::type&, + string const& key_prefix); + + static bool + has_a (semantics::type&, unsigned short flags); + protected: struct data; typedef cutl::shared_ptr data_ptr; @@ -199,6 +218,18 @@ public: typedef std::map type_map_type; protected: + typedef unsigned short column_type_flags; + + static column_type_flags const ctf_none = 0; + + // Default type should be NULL-able. + // + static column_type_flags const ctf_default_null = 0x01; + + // Get object id reference instead of object id. + // + static column_type_flags const ctf_object_id_ref = 0x02; + struct data { virtual ~data () {} @@ -206,10 +237,14 @@ protected: // Per-database customizable functionality. // public: + // Return empty string if there is no mapping. The second argument + // is the custom type or empty string if it is not specified. + // virtual string column_type_impl (semantics::type&, string const& type, - semantics::context*) const; + semantics::context&, + column_type_flags) const; keyword_set_type keyword_set_; type_map_type type_map_; diff --git a/odb/mysql/common.cxx b/odb/mysql/common.cxx index 17cae9e..d7e6187 100644 --- a/odb/mysql/common.cxx +++ b/odb/mysql/common.cxx @@ -33,130 +33,149 @@ namespace mysql semantics::type& t (type_override_ != 0 ? *type_override_ : m.type ()); - member_info mi (m, t, var, fq_type_override_); - if (comp_value (t)) { + member_info mi (m, t, var, fq_type_override_); pre (mi); traverse_composite (mi); + post (mi); } else if (container (t)) { + member_info mi (m, t, var, fq_type_override_); pre (mi); traverse_container (mi); + post (mi); } else { sql_type const& st (db_type (m, key_prefix_)); - mi.st = &st; - pre (mi); - switch (st.type) + if (semantics::class_* c = object_pointer (m, key_prefix_)) { - // Integral types. - // - case sql_type::TINYINT: - case sql_type::SMALLINT: - case sql_type::MEDIUMINT: - case sql_type::INT: - case sql_type::BIGINT: - { - traverse_integer (mi); - break; - } - - // Float types. - // - case sql_type::FLOAT: - case sql_type::DOUBLE: - { - traverse_float (mi); - break; - } - case sql_type::DECIMAL: - { - traverse_decimal (mi); - break; - } - - // Data-time types. + member_info mi (m, id_member (*c).type (), var, fq_type_override_); + mi.st = &st; + pre (mi); + traverse_object_pointer (mi); + post (mi); + } + else + { + member_info mi (m, t, var, fq_type_override_); + mi.st = &st; + pre (mi); + traverse_simple (mi); + post (mi); + } + } + } + + void member_base:: + traverse_simple (member_info& mi) + { + switch (mi.st->type) + { + // Integral types. + // + case sql_type::TINYINT: + case sql_type::SMALLINT: + case sql_type::MEDIUMINT: + case sql_type::INT: + case sql_type::BIGINT: + { + traverse_integer (mi); + break; + } + + // Float types. + // + case sql_type::FLOAT: + case sql_type::DOUBLE: + { + traverse_float (mi); + break; + } + case sql_type::DECIMAL: + { + traverse_decimal (mi); + break; + } + + // Data-time types. + // + case sql_type::DATE: + case sql_type::TIME: + case sql_type::DATETIME: + case sql_type::TIMESTAMP: + case sql_type::YEAR: + { + traverse_date_time (mi); + break; + } + + // String and binary types. + // + case sql_type::CHAR: + case sql_type::VARCHAR: + case sql_type::TINYTEXT: + case sql_type::TEXT: + case sql_type::MEDIUMTEXT: + case sql_type::LONGTEXT: + { + // For string types the limit is in characters rather + // than in bytes. The fixed-length pre-allocated buffer + // optimization can only be used for 1-byte encodings. + // To support this we will need the character encoding + // in sql_type. // - case sql_type::DATE: - case sql_type::TIME: - case sql_type::DATETIME: - case sql_type::TIMESTAMP: - case sql_type::YEAR: - { - traverse_date_time (mi); - break; - } - - // String and binary types. + traverse_long_string (mi); + break; + } + case sql_type::BINARY: + case sql_type::TINYBLOB: + { + // BINARY's range is always 255 or less from MySQL 5.0.3. + // TINYBLOB can only store up to 255 bytes. // - case sql_type::CHAR: - case sql_type::VARCHAR: - case sql_type::TINYTEXT: - case sql_type::TEXT: - case sql_type::MEDIUMTEXT: - case sql_type::LONGTEXT: - { - // For string types the limit is in characters rather - // than in bytes. The fixed-length pre-allocated buffer - // optimization can only be used for 1-byte encodings. - // To support this we will need the character encoding - // in sql_type. - // - traverse_long_string (mi); - break; - } - case sql_type::BINARY: - case sql_type::TINYBLOB: - { - // BINARY's range is always 255 or less from MySQL 5.0.3. - // TINYBLOB can only store up to 255 bytes. - // + traverse_short_string (mi); + break; + } + case sql_type::VARBINARY: + case sql_type::BLOB: + case sql_type::MEDIUMBLOB: + case sql_type::LONGBLOB: + { + if (mi.st->range && mi.st->range_value <= 255) traverse_short_string (mi); - break; - } - case sql_type::VARBINARY: - case sql_type::BLOB: - case sql_type::MEDIUMBLOB: - case sql_type::LONGBLOB: - { - if (st.range && st.range_value <= 255) - traverse_short_string (mi); - else - traverse_long_string (mi); - - break; - } - - // Other types. - // - case sql_type::BIT: - { - traverse_bit (mi); - break; - } - case sql_type::ENUM: - { - traverse_enum (mi); - break; - } - case sql_type::SET: - { - traverse_set (mi); - break; - } - case sql_type::invalid: - { - assert (false); - break; - } + else + traverse_long_string (mi); + + break; } - } - post (mi); + // Other types. + // + case sql_type::BIT: + { + traverse_bit (mi); + break; + } + case sql_type::ENUM: + { + traverse_enum (mi); + break; + } + case sql_type::SET: + { + traverse_set (mi); + break; + } + case sql_type::invalid: + { + assert (false); + break; + } + } } // diff --git a/odb/mysql/common.hxx b/odb/mysql/common.hxx index 31c6962..fb1f2a3 100644 --- a/odb/mysql/common.hxx +++ b/odb/mysql/common.hxx @@ -46,7 +46,13 @@ namespace mysql string fq_type () const { - return fq_type_.empty () ? t.fq_name (m.belongs ().hint ()) : fq_type_; + // 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. + // + return fq_type_.empty () + ? m.type ().fq_name (m.belongs ().hint ()) + : fq_type_; } string const& fq_type_; @@ -81,6 +87,15 @@ namespace mysql } virtual void + traverse_object_pointer (member_info& mi) + { + traverse_simple (mi); + } + + virtual void + traverse_simple (member_info&); + + virtual void traverse_integer (member_info&) { } diff --git a/odb/mysql/context.cxx b/odb/mysql/context.cxx index 4803da2..de3eed0 100644 --- a/odb/mysql/context.cxx +++ b/odb/mysql/context.cxx @@ -26,28 +26,28 @@ namespace mysql type_map_entry type_map[] = { - {"bool", "TINYINT(1) NOT NULL", 0}, + {"bool", "TINYINT(1)", 0}, - {"char", "TINYINT NOT NULL", 0}, - {"signed char", "TINYINT NOT NULL", 0}, - {"unsigned char", "TINYINT UNSIGNED NOT NULL", 0}, + {"char", "TINYINT", 0}, + {"signed char", "TINYINT", 0}, + {"unsigned char", "TINYINT UNSIGNED", 0}, - {"short int", "SMALLINT NOT NULL", 0}, - {"short unsigned int", "SMALLINT UNSIGNED NOT NULL", 0}, + {"short int", "SMALLINT", 0}, + {"short unsigned int", "SMALLINT UNSIGNED", 0}, - {"int", "INT NOT NULL", 0}, - {"unsigned int", "INT UNSIGNED NOT NULL", 0}, + {"int", "INT", 0}, + {"unsigned int", "INT UNSIGNED", 0}, - {"long int", "BIGINT NOT NULL", 0}, - {"long unsigned int", "BIGINT UNSIGNED NOT NULL", 0}, + {"long int", "BIGINT", 0}, + {"long unsigned int", "BIGINT UNSIGNED", 0}, - {"long long int", "BIGINT NOT NULL", 0}, - {"long long unsigned int", "BIGINT UNSIGNED NOT NULL", 0}, + {"long long int", "BIGINT", 0}, + {"long long unsigned int", "BIGINT UNSIGNED", 0}, - {"float", "FLOAT NOT NULL", 0}, - {"double", "DOUBLE NOT NULL", 0}, + {"float", "FLOAT", 0}, + {"double", "DOUBLE", 0}, - {"::std::string", "TEXT NOT NULL", "VARCHAR(255) NOT NULL"} + {"::std::string", "TEXT", "VARCHAR (255)"} }; } @@ -145,8 +145,10 @@ namespace mysql virtual void traverse_composite (member_info& mi) { + // Reset any overrides. + // if (!hg_.r_) - hg_.r_ = hg_.dispatch (dynamic_cast (mi.t)); + hg_.r_ = context::grow (dynamic_cast (mi.t)); } virtual void @@ -216,11 +218,12 @@ namespace mysql string context::data:: column_type_impl (semantics::type& t, string const& type, - semantics::context* ctx) const + semantics::context& ctx, + column_type_flags f) const { - string r (::context::data::column_type_impl (t, type, ctx)); + string r (::context::data::column_type_impl (t, type, ctx, f)); - if (!r.empty () && ctx != 0 && ctx->count ("auto")) + if (!r.empty () && ctx.count ("auto") && (f & ctf_object_id_ref) == 0) r += " AUTO_INCREMENT"; return r; diff --git a/odb/mysql/context.hxx b/odb/mysql/context.hxx index 9a6beec..4ac36ca 100644 --- a/odb/mysql/context.hxx +++ b/odb/mysql/context.hxx @@ -105,7 +105,8 @@ namespace mysql virtual string column_type_impl (semantics::type&, string const& type, - semantics::context*) const; + semantics::context&, + column_type_flags) const; }; private: diff --git a/odb/mysql/header.cxx b/odb/mysql/header.cxx index 79b8d67..130a2ba 100644 --- a/odb/mysql/header.cxx +++ b/odb/mysql/header.cxx @@ -525,24 +525,25 @@ namespace mysql { case ck_ordered: { - os << "init (index_type&, value_type&, const data_image_type&);"; + os << "init (index_type&, value_type&, "; break; } case ck_map: case ck_multimap: { - os << "init (key_type&, value_type&, const data_image_type&);"; + os << "init (key_type&, value_type&, "; break; } case ck_set: case ck_multiset: { - os << "init (value_type&, const data_image_type&);"; + os << "init (value_type&, "; break; } } - os << endl; + os << "const data_image_type&, database&);" + << endl; // insert_one // @@ -801,7 +802,7 @@ namespace mysql // init (object, image) // os << "static void" << endl - << "init (object_type&, const image_type&);" + << "init (object_type&, const image_type&, database&);" << endl; // persist () @@ -895,7 +896,7 @@ namespace mysql // init (object, image) // os << "static void" << endl - << "init (value_type&, const image_type&);" + << "init (value_type&, const image_type&, database&);" << endl; os << "};"; diff --git a/odb/mysql/schema.cxx b/odb/mysql/schema.cxx index 1dfeb1f..0c004ad 100644 --- a/odb/mysql/schema.cxx +++ b/odb/mysql/schema.cxx @@ -18,8 +18,8 @@ namespace mysql struct object_columns: object_columns_base, context { - object_columns (context& c) - : object_columns_base (c), context (c) + object_columns (context& c, string const& prefix = string ()) + : object_columns_base (c), context (c), prefix_ (prefix) { } @@ -29,11 +29,21 @@ namespace mysql if (!first) os << "," << endl; - os << " `" << name << "` " << column_type (m); + os << " `" << name << "` " << column_type (m, prefix_); if (m.count ("id")) os << " PRIMARY KEY"; + + using semantics::class_; + if (class_* c = object_pointer (m, prefix_)) + { + os << " REFERENCES `" << table_name (*c) << "` (`" << + column_name (id_member (*c)) << "`)"; + } } + + private: + string prefix_; }; struct member_create: object_members_base, context @@ -67,7 +77,7 @@ namespace mysql // object_id (simple value) // string id_name (column_name (m, "id", "object_id")); - os << " `" << id_name << "` " << column_type (id_member_); + os << " `" << id_name << "` " << column_type (id_member_, "ref"); // index (simple value) // @@ -95,8 +105,9 @@ namespace mysql } else { + object_columns oc (*this, "key"); string const& name (column_name (m, "key", "key")); - os << " `" << name << "` " << column_type (m, "key"); + oc.column (m, name, true); } } @@ -112,8 +123,9 @@ namespace mysql } else { + object_columns oc (*this, "value"); string const& name (column_name (m, "value", "value")); - os << " `" << name << "` " << column_type (m, "value"); + oc.column (m, name, true); } } diff --git a/odb/mysql/source.cxx b/odb/mysql/source.cxx index 2f65de6..8351ad5 100644 --- a/odb/mysql/source.cxx +++ b/odb/mysql/source.cxx @@ -516,19 +516,6 @@ namespace mysql { if (container (mi.t)) return; - else if (comp_value (mi.t)) - traits = "composite_value_traits< " + mi.fq_type () + " >"; - else - { - type = mi.fq_type (); - image_type = member_image_type_.image_type (mi.m); - db_type = member_database_type_.database_type (mi.m); - - traits = "mysql::value_traits< " - + type + ", " - + image_type + ", " - + db_type + " >"; - } if (!member_override_.empty ()) member = member_override_; @@ -540,6 +527,65 @@ namespace mysql os << "// " << name << endl << "//" << endl; } + + if (comp_value (mi.t)) + traits = "composite_value_traits< " + mi.fq_type () + " >"; + else + { + if (semantics::class_* c = object_pointer (mi.m, key_prefix_)) + { + type = "obj_traits::id_type"; + image_type = member_image_type_.image_type (mi.m); + db_type = member_database_type_.database_type (mi.m); + + // Handle NULL pointers and extract the id. + // + os << "{" + << "typedef object_traits< " << c->fq_name () << + " > obj_traits;" + << "typedef pointer_traits< " << mi.fq_type () << + " > ptr_traits;" + << endl + << "bool is_null (ptr_traits::null_ptr (" << member << "));" + << "if (!is_null)" + << "{" + << "const " << type << "& id (" << endl + << "obj_traits::id (ptr_traits::get_ref (" << member << ")));" + << endl; + + member = "id"; + } + else + { + type = mi.fq_type (); + image_type = member_image_type_.image_type (mi.m); + db_type = member_database_type_.database_type (mi.m); + + os << "{" + << "bool is_null;"; + } + + traits = "mysql::value_traits<\n " + + type + ",\n " + + image_type + ",\n " + + db_type + " >"; + } + } + + virtual void + post (member_info& mi) + { + if (container (mi.t)) + return; + + if (!comp_value (mi.t)) + { + if (object_pointer (mi.m, key_prefix_)) + os << "}"; + + os << "i." << mi.var << "null = is_null;" + << "}"; + } } virtual void @@ -555,23 +601,15 @@ namespace mysql virtual void traverse_integer (member_info& mi) { - os << "{" - << "bool is_null;" - << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");" - << "i." << mi.var << "null = is_null;" - << "}"; + os << traits << "::set_image (" << endl + << "i." << mi.var << "value, is_null, " << member << ");"; } virtual void traverse_float (member_info& mi) { - os << "{" - << "bool is_null;" - << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");" - << "i." << mi.var << "null = is_null;" - << "}"; + os << traits << "::set_image (" << endl + << "i." << mi.var << "value, is_null, " << member << ");"; } virtual void @@ -579,9 +617,7 @@ namespace mysql { // @@ Optimization: can remove growth check if buffer is fixed. // - os << "{" - << "bool is_null;" - << "std::size_t size;" + os << "std::size_t size;" << "std::size_t cap (i." << mi.var << "value.capacity ());" << traits << "::set_image (" << endl << "i." << mi.var << "value," << endl @@ -589,20 +625,14 @@ namespace mysql << "is_null," << endl << member << ");" << "i." << mi.var << "size = static_cast (size);" - << "i." << mi.var << "null = is_null;" - << "grew = grew || (cap != i." << mi.var << "value.capacity ());" - << "}"; + << "grew = grew || (cap != i." << mi.var << "value.capacity ());"; } virtual void traverse_date_time (member_info& mi) { - os << "{" - << "bool is_null;" - << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");" - << "i." << mi.var << "null = is_null;" - << "}"; + os << traits << "::set_image (" << endl + << "i." << mi.var << "value, is_null, " << member << ");"; } virtual void @@ -610,9 +640,7 @@ namespace mysql { // @@ Optimization: can remove growth check if buffer is fixed. // - os << "{" - << "bool is_null;" - << "std::size_t size;" + os << "std::size_t size;" << "std::size_t cap (i." << mi.var << "value.capacity ());" << traits << "::set_image (" << endl << "i." << mi.var << "value," << endl @@ -620,17 +648,13 @@ namespace mysql << "is_null," << endl << member << ");" << "i." << mi.var << "size = static_cast (size);" - << "i." << mi.var << "null = is_null;" - << "grew = grew || (cap != i." << mi.var << "value.capacity ());" - << "}"; + << "grew = grew || (cap != i." << mi.var << "value.capacity ());"; } virtual void traverse_long_string (member_info& mi) { - os << "{" - << "bool is_null;" - << "std::size_t size;" + os << "std::size_t size;" << "std::size_t cap (i." << mi.var << "value.capacity ());" << traits << "::set_image (" << endl << "i." << mi.var << "value," << endl @@ -638,9 +662,7 @@ namespace mysql << "is_null," << endl << member << ");" << "i." << mi.var << "size = static_cast (size);" - << "i." << mi.var << "null = is_null;" - << "grew = grew || (cap != i." << mi.var << "value.capacity ());" - << "}"; + << "grew = grew || (cap != i." << mi.var << "value.capacity ());"; } virtual void @@ -648,18 +670,14 @@ namespace mysql { // Represented as a BLOB. // - os << "{" - << "bool is_null;" - << "std::size_t size;" + os << "std::size_t size;" << traits << "::set_image (" << endl << "i." << mi.var << "value," << endl << "sizeof (i." << mi.var << "value)," << endl << "size," << endl << "is_null," << endl << member << ");" - << "i." << mi.var << "size = static_cast (size);" - << "i." << mi.var << "null = is_null;" - << "}"; + << "i." << mi.var << "size = static_cast (size);"; } virtual void @@ -667,9 +685,7 @@ namespace mysql { // Represented as a string. // - os << "{" - << "bool is_null;" - << "std::size_t size;" + os << "std::size_t size;" << "std::size_t cap (i." << mi.var << "value.capacity ());" << traits << "::set_image (" << endl << "i." << mi.var << "value," << endl @@ -677,9 +693,7 @@ namespace mysql << "is_null," << endl << member << ");" << "i." << mi.var << "size = static_cast (size);" - << "i." << mi.var << "null = is_null;" - << "grew = grew || (cap != i." << mi.var << "value.capacity ());" - << "}"; + << "grew = grew || (cap != i." << mi.var << "value.capacity ());"; } virtual void @@ -687,9 +701,7 @@ namespace mysql { // Represented as a string. // - os << "{" - << "bool is_null;" - << "std::size_t size;" + os << "std::size_t size;" << "std::size_t cap (i." << mi.var << "value.capacity ());" << traits << "::set_image (" << endl << "i." << mi.var << "value," << endl @@ -697,9 +709,7 @@ namespace mysql << "is_null," << endl << member << ");" << "i." << mi.var << "size = static_cast (size);" - << "i." << mi.var << "null = is_null;" - << "grew = grew || (cap != i." << mi.var << "value.capacity ());" - << "}"; + << "grew = grew || (cap != i." << mi.var << "value.capacity ());"; } private: @@ -766,19 +776,6 @@ namespace mysql { if (container (mi.t)) return; - else if (comp_value (mi.t)) - traits = "composite_value_traits< " + mi.fq_type () + " >"; - else - { - type = mi.fq_type (); - image_type = member_image_type_.image_type (mi.m); - db_type = member_database_type_.database_type (mi.m); - - traits = "mysql::value_traits< " - + type + ", " - + image_type + ", " - + db_type + " >"; - } if (!member_override_.empty ()) member = member_override_; @@ -790,13 +787,75 @@ namespace mysql os << "// " << name << endl << "//" << endl; } + + if (comp_value (mi.t)) + traits = "composite_value_traits< " + mi.fq_type () + " >"; + else + { + if (semantics::class_* c = object_pointer (mi.m, key_prefix_)) + { + type = "obj_traits::id_type"; + image_type = member_image_type_.image_type (mi.m); + db_type = member_database_type_.database_type (mi.m); + + // Handle NULL pointers and extract the id. + // + os << "{" + << "typedef object_traits< " << c->fq_name () << + " > obj_traits;" + << "typedef pointer_traits< " << mi.fq_type () << + " > ptr_traits;" + << endl + << "if (i." << mi.var << "null)" << endl + << member << " = ptr_traits::pointer_type ();" + << "else" + << "{" + << type << " id;"; + + member = "id"; + } + else + { + type = mi.fq_type (); + image_type = member_image_type_.image_type (mi.m); + db_type = member_database_type_.database_type (mi.m); + } + + traits = "mysql::value_traits<\n " + + type + ",\n " + + image_type + ",\n " + + db_type + " >"; + } + } + + virtual void + post (member_info& mi) + { + if (container (mi.t)) + return; + + if (!comp_value (mi.t) && object_pointer (mi.m, key_prefix_)) + { + member = member_override_.empty () + ? "o." + mi.m.name () + : member_override_; + + 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));" + << "}" + << "}"; + } } virtual void traverse_composite (member_info& mi) { os << traits << "::init (" << member << ", i." << - mi.var << "value);" + mi.var << "value, db);" << endl; } @@ -924,7 +983,8 @@ namespace mysql { os << "// " << c.name () << " base" << endl << "//" << endl - << "composite_value_traits< " << c.fq_name () << " >::init (o, i);" + << "composite_value_traits< " << c.fq_name () << + " >::init (o, i, db);" << endl; } }; @@ -1232,8 +1292,10 @@ namespace mysql size_t index (0); os << "bool " << scope << "::" << endl - << "grow (data_image_type&" << (grow ? " i" : "") << ", my_bool* e)" + << "grow (data_image_type& i, my_bool* e)" << "{" + << "ODB_POTENTIALLY_UNUSED (i);" + << endl << "bool r (false);" << endl; @@ -1339,8 +1401,11 @@ namespace mysql { case ck_ordered: { - os << "init (index_type& j, value_type& v, const data_image_type& i)" + os << "init (index_type& j, value_type& v, " << + "const data_image_type& i, database& db)" << "{" + << "ODB_POTENTIALLY_UNUSED (db);" + << endl << "// index" << endl << "//" << endl; @@ -1353,8 +1418,11 @@ namespace mysql case ck_map: case ck_multimap: { - os << "init (key_type& k, value_type& v, const data_image_type& i)" + os << "init (key_type& k, value_type& v, " << + "const data_image_type& i, database& db)" << "{" + << "ODB_POTENTIALLY_UNUSED (db);" + << endl << "// key" << endl << "//" << endl; @@ -1366,8 +1434,11 @@ namespace mysql case ck_set: case ck_multiset: { - os << "init (value_type& v, const data_image_type& i)" - << "{"; + os << "init (value_type& v, const data_image_type& i, " << + "database& db)" + << "{" + << "ODB_POTENTIALLY_UNUSED (db);" + << endl; break; } } @@ -1375,6 +1446,9 @@ namespace mysql os << "// value" << endl << "//" << endl; { + // If the value is an object pointer, pass the id type as a + // type override. + // init_value_member im ( *this, "value_", "v", vt, "value_type", "value"); im.traverse (m); @@ -1485,19 +1559,19 @@ namespace mysql { case ck_ordered: { - os << "init (i, v, di);"; + os << "init (i, v, di, sts.connection ().database ());"; break; } case ck_map: case ck_multimap: { - os << "init (k, v, di);"; + os << "init (k, v, di, sts.connection ().database ());"; break; } case ck_set: case ck_multiset: { - os << "init (v, di);"; + os << "init (v, di, sts.connection ().database ());"; break; } } @@ -1514,9 +1588,9 @@ namespace mysql << "{" << "if (grow (di, sts.data_image_error ()))" << "{" - << "binding& db (sts.data_image_binding ());" - << "bind (db.bind, 0, di);" - << "db.version++;" + << "binding& b (sts.data_image_binding ());" + << "bind (b.bind, 0, di);" + << "b.version++;" << "st.refetch ();" << "}" << "}"; @@ -1589,8 +1663,15 @@ namespace mysql << "cb.version++;" << "}" << "select_statement& st (sts.select_all_statement ());" - << "st.execute ();" - << "select_statement::result r (st.fetch ());"; + << "st.execute ();"; + + // If we are loading eager object pointers, cache the result + // since we will be loading other objects. + // + if (is_a (m, eager_pointer, vt, "value") || has_a (vt, eager_pointer)) + os << "st.cache ();"; + + os << "select_statement::result r (st.fetch ());"; if (grow) os << endl @@ -1971,8 +2052,10 @@ namespace mysql // grow () // os << "bool " << traits << "::" << endl - << "grow (image_type&" << (grow ? " i" : "") << ", my_bool* e)" + << "grow (image_type& i, my_bool* e)" << "{" + << "ODB_POTENTIALLY_UNUSED (i);" + << endl << "bool r (false);" << endl; @@ -2023,8 +2106,10 @@ namespace mysql // init (object, image) // os << "void " << traits << "::" << endl - << "init (object_type& o, const image_type& i)" - << "{"; + << "init (object_type& o, const image_type& i, database& db)" + << "{" + << "ODB_POTENTIALLY_UNUSED (db);" + << endl; inherits (c, init_value_base_inherits_); names (c, init_value_member_names_); @@ -2163,7 +2248,7 @@ namespace mysql { os << traits << "::pointer_type" << endl << traits << "::" << endl - << "find (database&, const id_type& id)" + << "find (database& db, const id_type& id)" << "{" << "using namespace mysql;" << endl @@ -2178,7 +2263,7 @@ namespace mysql "pointer_type >::create ());" << "pointer_traits< pointer_type >::guard_type g (p);" << "object_type& obj (pointer_traits< pointer_type >::get_ref (p));" - << "init (obj, sts.image ());"; + << "init (obj, sts.image (), db);"; if (containers) { @@ -2197,7 +2282,7 @@ namespace mysql } os << "bool " << traits << "::" << endl - << "find (database&, const id_type& id, object_type& obj)" + << "find (database& db, const id_type& id, object_type& obj)" << "{" << "using namespace mysql;" << endl @@ -2208,7 +2293,7 @@ namespace mysql << "bool grew (false);" << "if (find (sts, id, grew))" << "{" - << "init (obj, sts.image ());"; + << "init (obj, sts.image (), db);"; if (containers) { @@ -2303,7 +2388,6 @@ namespace mysql traverse_value (type& c) { bool columns (column_count (c) != 0); - bool grow (columns && context::grow (c)); string const& type (c.fq_name ()); string traits ("access::composite_value_traits< " + type + " >"); @@ -2315,9 +2399,10 @@ namespace mysql // grow () // os << "bool " << traits << "::" << endl - << "grow (image_type&" << (grow ? " i" : "") << ", " << - "my_bool*" << (columns ? " e" : "") << ")" + << "grow (image_type& i, my_bool*" << (columns ? " e" : "") << ")" << "{" + << "ODB_POTENTIALLY_UNUSED (i);" + << endl << "bool r (false);" << endl; @@ -2360,8 +2445,11 @@ namespace mysql // os << "void " << traits << "::" << endl << "init (value_type&" << (columns ? " o" : "") << ", " << - "const image_type&" << (columns ? " i" : "") << ")" - << "{"; + "const image_type&" << (columns ? " i" : "") << ", " << + "database& db)" + << "{" + << "ODB_POTENTIALLY_UNUSED (db);" + << endl; inherits (c, init_value_base_inherits_); names (c, init_value_member_names_); @@ -2426,9 +2514,16 @@ namespace mysql << "#include " << endl; if (ctx.options.generate_query ()) - ctx.os << "#include " << endl - << endl - << "#include " << endl; + ctx.os << "#include " << endl; + + ctx.os << endl; + + // Details includes. + // + ctx.os << "#include " << endl; + + if (ctx.options.generate_query ()) + ctx.os << "#include " << endl; ctx.os << endl; diff --git a/odb/type-processor.cxx b/odb/type-processor.cxx index 921a90b..9de59b4 100644 --- a/odb/type-processor.cxx +++ b/odb/type-processor.cxx @@ -27,6 +27,20 @@ namespace throw generation_failed (); } + // Find pointer traits. + // + pointer_traits_ = lookup_qualified_name ( + odb, get_identifier ("pointer_traits"), true, false); + + if (container_traits_ == error_mark_node || + !DECL_CLASS_TEMPLATE_P (pointer_traits_)) + { + os << unit.file () << ": error: unable to resolve pointer_traits " + << "in the odb namespace" << endl; + + throw generation_failed (); + } + // Find the access class. // tree access = lookup_qualified_name ( @@ -70,7 +84,7 @@ namespace if (comp_value (t)) return; - string type; + string type, ref_type; if (m.count ("type")) type = m.get ("type"); @@ -78,11 +92,39 @@ namespace if (type.empty () && t.count ("type")) type = t.get ("type"); - type = column_type_impl (t, type, &m); + if (semantics::class_* c = process_object_pointer (m, t)) + { + // This is an object pointer. The column type is the pointed-to + // object id type. Except by default it can be NULL. + // + semantics::data_member& id (id_member (*c)); + semantics::type& idt (id.type ()); + + if (type.empty () && id.count ("type")) + type = id.get ("type"); + + if (type.empty () && idt.count ("type")) + type = idt.get ("type"); + + type = data_->column_type_impl ( + idt, type, id, ctf_default_null | ctf_object_id_ref); + } + else + { + string orig (type); + type = data_->column_type_impl (t, orig, m, ctf_none); + + if (m.count ("id")) + ref_type = data_->column_type_impl (t, orig, m, ctf_object_id_ref); + } if (!type.empty ()) { m.set ("column-type", type); + + if (!ref_type.empty ()) + m.set ("ref-column-type", ref_type); + return; } @@ -108,29 +150,50 @@ namespace void process_container_value (semantics::type& t, + semantics::data_member& m, string const& prefix, - semantics::data_member& m) + bool obj_ptr) { if (comp_value (t)) return; string type; + semantics::type& ct (m.type ()); // Custom mapping can come from these places (listed in the order - // of priority): member, container type, value type. + // of priority): member, container type, value type. To complicate + // things a bit, for object references, it can also come from the + // member and value type of the id member. // if (m.count (prefix + "-type")) type = m.get (prefix + "-type"); - semantics::type& ct (m.type ()); - if (type.empty () && ct.count (prefix + "-type")) type = ct.get (prefix + "-type"); if (type.empty () && t.count ("type")) type = t.get ("type"); - type = column_type_impl (t, type, 0); + semantics::class_* c; + 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. + // + semantics::data_member& id (id_member (*c)); + semantics::type& idt (id.type ()); + + if (type.empty () && id.count ("type")) + type = id.get ("type"); + + if (type.empty () && idt.count ("type")) + type = idt.get ("type"); + + type = data_->column_type_impl ( + idt, type, id, ctf_default_null | ctf_object_id_ref); + } + else + type = data_->column_type_impl (t, type, m, ctf_none); if (!type.empty ()) { @@ -185,35 +248,9 @@ namespace } else { - tree args (make_tree_vec (1)); - TREE_VEC_ELT (args, 0) = t.tree_node (); - - // This step should succeed regardles of whether there is a - // container traits specialization for this type. - // - tree inst ( - lookup_template_class ( - container_traits_, args, 0, 0, 0, tf_warning_or_error)); - - if (inst == error_mark_node) - { - // Diagnostics has already been issued by lookup_template_class. - // - throw generation_failed (); - } - - inst = TYPE_MAIN_VARIANT (inst); - - // The instantiation may already be complete if it matches a - // (complete) specialization. - // - if (!COMPLETE_TYPE_P (inst)) - inst = instantiate_class_template (inst); + tree inst (instantiate_template (container_traits_, t.tree_node ())); - // If we cannot instantiate this type, assume there is no suitable - // container traits specialization for it. - // - if (inst == error_mark_node || !COMPLETE_TYPE_P (inst)) + if (inst == 0) return false; // @@ This points to the primary template, not the specialization. @@ -363,18 +400,105 @@ namespace // Process member data. // - process_container_value (*vt, "value", m); + process_container_value (*vt, m, "value", true); if (it != 0) - process_container_value (*it, "index", m); + process_container_value (*it, m, "index", false); if (kt != 0) - process_container_value (*kt, "key", m); + process_container_value (*kt, m, "key", false); return true; } + semantics::class_* + process_object_pointer (semantics::data_member& m, + semantics::type& t, + string const& kp = string ()) + { + // The overall idea is as follows: try to instantiate the pointer + // traits class template. If we are successeful, then get the + // element type and see if it is an object. + // + using semantics::class_; + + tree inst (instantiate_template (pointer_traits_, t.tree_node ())); + + if (inst == 0) + return 0; + + // Get the element type. + // + tree tn (0); + try + { + tree decl ( + lookup_qualified_name ( + inst, get_identifier ("element_type"), true, false)); + + if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL) + throw generation_failed (); + + tn = TYPE_MAIN_VARIANT (TREE_TYPE (decl)); + } + catch (generation_failed const&) + { + os << m.file () << ":" << m.line () << ":" << m.column () << ": " + << "error: odb::pointer_traits specialization does not define " + << "the element_type type" << endl; + throw; + } + + if (class_* c = dynamic_cast (unit.find (tn))) + { + if (c->count ("object")) + { + m.set (kp + (kp.empty () ? "": "-") + "object-pointer", c); + return c; + } + } + + return 0; + } + + tree + instantiate_template (tree t, tree arg) + { + tree args (make_tree_vec (1)); + TREE_VEC_ELT (args, 0) = arg; + + // This step should succeed regardles of whether there is a + // container traits specialization for this type. + // + tree inst ( + lookup_template_class (t, args, 0, 0, 0, tf_warning_or_error)); + + if (inst == error_mark_node) + { + // Diagnostics has already been issued by lookup_template_class. + // + throw generation_failed (); + } + + inst = TYPE_MAIN_VARIANT (inst); + + // The instantiation may already be complete if it matches a + // (complete) specialization or was used before. + // + if (!COMPLETE_TYPE_P (inst)) + inst = instantiate_class_template (inst); + + // If we cannot instantiate this type, assume there is no suitable + // specialization for it. + // + if (inst == error_mark_node || !COMPLETE_TYPE_P (inst)) + return 0; + + return inst; + } + private: + tree pointer_traits_; tree container_traits_; }; -- cgit v1.1