From f8b3ee6d42f5112c4e66a07cc7fdba43ce66aacd Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 27 Apr 2012 13:32:45 +0200 Subject: Support for NULL value semantics for composite values --- NEWS | 6 ++++- doc/manual.xhtml | 32 ++++++++++++++++++------- odb/relational/source.hxx | 61 ++++++++++++++++++++++++++++++++++------------- odb/validator.cxx | 54 +++++++++++++++++++++++++++++++++++++---- 4 files changed, 123 insertions(+), 30 deletions(-) diff --git a/NEWS b/NEWS index 8302f3b..6979130 100644 --- a/NEWS +++ b/NEWS @@ -45,6 +45,10 @@ Version 1.9.0 namespace. For more information, refer to Section 12.5.1, "pointer" and Section 12.5.2, "table" in the ODB manual. + * Support for the NULL value semantics for composite values. For more + information, refer to Section 7.3, "Pointers and NULL Value Semantics" + in the ODB manual. + * Session support is now optional and is disabled by default. This is a backwards-incompatible change. Session support can be enabled on the per object basis or at the namespace level using the new session pragma. @@ -58,7 +62,7 @@ Version 1.9.0 function for the *_persist, *_update, and *_erase events is always called on the constant object reference while for the *_load events -- always on the unrestricted reference. For more information, refer to Section 12.1.7, - "Callback" in the ODB manual. + "callback" in the ODB manual. * New function, transaction::reset(), allows the reuse of the same transaction instance to complete several database transactions. For more diff --git a/doc/manual.xhtml b/doc/manual.xhtml index dd7c649..c32cc37 100644 --- a/doc/manual.xhtml +++ b/doc/manual.xhtml @@ -6326,7 +6326,7 @@ CREATE TABLE person_nickname ( "null/not_null").

To properly support the NULL semantics, the - C++ value type must have a notion or a NULL + C++ value type must have a notion of a NULL value or a similar special state concept. Most basic C++ types, such as int or std::string, do not have this notion and therefore cannot be used directly @@ -6481,18 +6481,17 @@ class person Qt), provide support for smart pointers found in these frameworks and libraries (Part III, "Profiles").

-

Currently, ODB supports the NULL semantics only - for simple values. In future versions this support will be extended - to composite values and containers. With this limitation in mind, - we can still use smart pointers in data members of composite value - and container types. The only restriction is that these pointers - must not be NULL. For example:

+

ODB also supports the NULL semantics for composite + values. In the relational database the NULL composite + value is translated to NULL values for all the simple + data members of this composite value. For example:

 #pragma db value
 struct name
 {
   std::string first_;
+  odb::nullable<std::string> middle_;
   std::string last_;
 };
 
@@ -6500,9 +6499,24 @@ struct name
 class person
 {
   ...
+  odb::nullable<name> name_;
+};
+  
+ +

ODB does not support the NULL semantics for containers. + This also means that a composite value that contains a container + cannot be NULL. With this limitation in mind, we can + still use smart pointers in data members of container types. The + only restriction is that these pointers must not be NULL. + For example:

+ +
+#pragma db object
+class person
+{
+  ...
 
-  std::auto_ptr<name> name_;
-  std::auto_ptr<std::vector<name> > aliases_;
+  std::auto_ptr<std::vector<std::string> > aliases_;
 };
   
diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx index 594274a..2f2a61e 100644 --- a/odb/relational/source.hxx +++ b/odb/relational/source.hxx @@ -1170,26 +1170,41 @@ namespace relational member = "o." + name; } - os << "{"; - - if (discriminator (mi.m)) - os << "const info_type& di (map->find (typeid (o)));" - << endl; - bool comp (composite (mi.t)); // If this is a wrapped composite value, then we need to "unwrap" - // it. For simple values this is taken care of by the value_traits + // it. If this is a NULL wrapper, then we also need to handle that. + // 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. + // The wrapper type, not the wrapped type. // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "get_ref (" + member + ")"; + string const& wt (mi.fq_type (false)); + + // If this is a NULL wrapper and the member can be NULL, then + // we need to handle the NULL value. + // + if (null (mi.m, key_prefix_) && + mi.wrapper->template get ("wrapper-null-handler")) + { + os << "if (wrapper_traits< " + wt + " >::get_null (" << + member << "))" << endl + << "composite_value_traits< " + mi.fq_type () + " >::" << + "set_null (i." << mi.var << "value, sk);" + << "else"; + } + + member = "wrapper_traits< " + wt + " >::get_ref (" + member + ")"; } + os << "{"; + + if (discriminator (mi.m)) + os << "const info_type& di (map->find (typeid (o)));" + << endl; + if (mi.ptr != 0) { // When handling a pointer, mi.t is the id type of the referenced @@ -1431,16 +1446,30 @@ namespace relational 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 this is a wrapped composite value, then we need to "unwrap" it. + // If this is a NULL wrapper, then we also need to handle that. 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. + // The wrapper type, not the wrapped type. + // + string const& wt (mi.fq_type (false)); + + // If this is a NULL wrapper and the member can be NULL, then + // we need to handle the NULL value. // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "set_ref (\n" + member + ")"; + if (null (mi.m, key_prefix_) && + mi.wrapper->template get ("wrapper-null-handler")) + { + os << "if (composite_value_traits< " + mi.fq_type () + " >::" << + "get_null (i." << mi.var << "value))" << endl + << "wrapper_traits< " + wt + " >::set_null (" << member + ");" + << "else" << endl; + } + + member = "wrapper_traits< " + wt + " >::set_ref (" + member + ")"; } if (mi.ptr != 0) diff --git a/odb/validator.cxx b/odb/validator.cxx index eac5086..b0a6492 100644 --- a/odb/validator.cxx +++ b/odb/validator.cxx @@ -42,9 +42,9 @@ namespace // Pass 1. // - struct data_member: traversal::data_member, context + struct data_member1: traversal::data_member, context { - data_member (bool& valid) + data_member1 (bool& valid) : valid_ (valid) { } @@ -776,7 +776,7 @@ namespace bool& valid_; value_type& vt_; - data_member member_; + data_member1 member_; traversal::names names_; }; @@ -804,6 +804,41 @@ namespace // Pass 2. // + struct data_member2: traversal::data_member, context + { + data_member2 (bool& valid) + : valid_ (valid) + { + } + + virtual void + traverse (type& m) + { + if (transient (m)) + return; + + if (null (m)) + { + if (semantics::class_* c = composite_wrapper (utype (m))) + { + if (has_a (*c, test_container)) + { + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: composite member containing containers cannot " + << "be null" << endl; + + os << c->file () << ":" << c->line () << ":" << c->column () << ":" + << " info: composite value type is defined here" << endl; + + valid_ = false; + } + } + } + } + + bool& valid_; + }; + struct object_no_id_members: object_members_base { object_no_id_members (bool& valid) @@ -985,10 +1020,13 @@ namespace { class2 (bool& valid) : valid_ (valid), + data_member_ (valid), object_no_id_members_ (valid), composite_id_members_ (valid), view_members_ (valid) { + *this >> data_member_names_ >> data_member_; + // Find the has_lt_operator function template.. // has_lt_operator_ = 0; @@ -1156,6 +1194,8 @@ namespace object_no_id_members_.traverse (c); } } + + names (c); } virtual void @@ -1164,16 +1204,22 @@ namespace // Make sure we don't have any containers or object pointers. // view_members_.traverse (c); + + names (c); } virtual void - traverse_composite (type&) + traverse_composite (type& c) { + names (c); } bool& valid_; tree has_lt_operator_; + data_member2 data_member_; + traversal::names data_member_names_; + object_no_id_members object_no_id_members_; composite_id_members composite_id_members_; view_members view_members_; -- cgit v1.1