summaryrefslogtreecommitdiff
path: root/odb
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2010-11-17 18:05:06 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2010-11-17 18:05:06 +0200
commit2fe7a723c0b98d23531a76d9c1a451e3a57bf0c5 (patch)
tree8bd0323a9114a405902b3dfc116f97307278ae2b /odb
parent302b804ec633889f26dc54d937d9becc09246152 (diff)
Add support for unidirectional object relationships
New test: common/relationship.
Diffstat (limited to 'odb')
-rw-r--r--odb/context.cxx64
-rw-r--r--odb/context.hxx59
-rw-r--r--odb/mysql/common.cxx227
-rw-r--r--odb/mysql/common.hxx17
-rw-r--r--odb/mysql/context.cxx41
-rw-r--r--odb/mysql/context.hxx3
-rw-r--r--odb/mysql/header.cxx13
-rw-r--r--odb/mysql/schema.cxx24
-rw-r--r--odb/mysql/source.cxx315
-rw-r--r--odb/type-processor.cxx200
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<semantics::data_member*> ("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<class_*> ("object-pointer", 0)
+ : m.get<class_*> (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<semantics::type*> ("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> data_ptr;
@@ -199,6 +218,18 @@ public:
typedef std::map<string, db_type_type> 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<semantics::class_&> (mi.t));
+ hg_.r_ = context::grow (dynamic_cast<semantics::class_&> (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<unsigned long> (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<unsigned long> (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<unsigned long> (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<unsigned long> (size);"
- << "i." << mi.var << "null = is_null;"
- << "}";
+ << "i." << mi.var << "size = static_cast<unsigned long> (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<unsigned long> (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<unsigned long> (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 <odb/mysql/exceptions.hxx>" << endl;
if (ctx.options.generate_query ())
- ctx.os << "#include <odb/mysql/result.hxx>" << endl
- << endl
- << "#include <odb/details/shared-ptr.hxx>" << endl;
+ ctx.os << "#include <odb/mysql/result.hxx>" << endl;
+
+ ctx.os << endl;
+
+ // Details includes.
+ //
+ ctx.os << "#include <odb/details/unused.hxx>" << endl;
+
+ if (ctx.options.generate_query ())
+ ctx.os << "#include <odb/details/shared-ptr.hxx>" << 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<string> ("type");
@@ -78,11 +92,39 @@ namespace
if (type.empty () && t.count ("type"))
type = t.get<string> ("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<string> ("type");
+
+ if (type.empty () && idt.count ("type"))
+ type = idt.get<string> ("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<string> (prefix + "-type");
- semantics::type& ct (m.type ());
-
if (type.empty () && ct.count (prefix + "-type"))
type = ct.get<string> (prefix + "-type");
if (type.empty () && t.count ("type"))
type = t.get<string> ("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<string> ("type");
+
+ if (type.empty () && idt.count ("type"))
+ type = idt.get<string> ("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<class_*> (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_;
};