aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2012-04-27 13:32:45 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2012-04-27 13:32:45 +0200
commitf8b3ee6d42f5112c4e66a07cc7fdba43ce66aacd (patch)
tree2f3d877c7174e345514fe01cf9e462c1cb18b7ac
parent15b1a95942518c84f8161c59820ea5d0e49a4e54 (diff)
Support for NULL value semantics for composite values
-rw-r--r--NEWS6
-rw-r--r--doc/manual.xhtml32
-rw-r--r--odb/relational/source.hxx61
-rw-r--r--odb/validator.cxx54
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 (
"<code>null</code>/<code>not_null</code>"</a>).</p>
<p>To properly support the <code>NULL</code> semantics, the
- C++ value type must have a notion or a <code>NULL</code>
+ C++ value type must have a notion of a <code>NULL</code>
value or a similar special state concept. Most basic
C++ types, such as <code>int</code> or <code>std::string</code>,
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 (<a href="#III">Part III, "Profiles"</a>).</p>
- <p>Currently, ODB supports the <code>NULL</code> 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 <code>NULL</code>. For example:</p>
+ <p>ODB also supports the <code>NULL</code> semantics for composite
+ values. In the relational database the <code>NULL</code> composite
+ value is translated to <code>NULL</code> values for all the simple
+ data members of this composite value. For example:</p>
<pre class="c++">
#pragma db value
struct name
{
std::string first_;
+ odb::nullable&lt;std::string> middle_;
std::string last_;
};
@@ -6500,9 +6499,24 @@ struct name
class person
{
...
+ odb::nullable&lt;name> name_;
+};
+ </pre>
+
+ <p>ODB does not support the <code>NULL</code> semantics for containers.
+ This also means that a composite value that contains a container
+ cannot be <code>NULL</code>. 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 <code>NULL</code>.
+ For example:</p>
+
+ <pre class="c++">
+#pragma db object
+class person
+{
+ ...
- std::auto_ptr&lt;name> name_;
- std::auto_ptr&lt;std::vector&lt;name> > aliases_;
+ std::auto_ptr&lt;std::vector&lt;std::string> > aliases_;
};
</pre>
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<bool> ("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<bool> ("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_;