summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2012-03-06 11:34:57 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2012-03-06 11:34:57 +0200
commitf573927d48a7ceb155cf681e7cbad1e5176b2c0b (patch)
tree57fcdf7d0ad8da94af7a5e234c2a0d10cff8cfce
parent96deed4d2ef7d23b6015c1564866b3e60519d57c (diff)
Detect situations where session is required but not used
Throw session_required.
-rw-r--r--NEWS5
-rw-r--r--doc/manual.xhtml56
-rw-r--r--odb/relational/source.hxx18
3 files changed, 79 insertions, 0 deletions
diff --git a/NEWS b/NEWS
index 4363f64..06879eb 100644
--- a/NEWS
+++ b/NEWS
@@ -38,6 +38,11 @@ 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.
+ * New exception, odb::session_required, is thrown when ODB detects that
+ correctly loading a bidirectional object relationship requires a session
+ but one is not used. For more information, refer to Section 6.2,
+ "Bidirectional Relationships" in the ODB manual.
+
Version 1.8.0
* Support for the Microsoft SQL Server database. The provided connection
diff --git a/doc/manual.xhtml b/doc/manual.xhtml
index a36481d..6257cf7 100644
--- a/doc/manual.xhtml
+++ b/doc/manual.xhtml
@@ -3648,6 +3648,12 @@ namespace odb
what () const throw ();
};
+ struct session_required: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
// Database operations exceptions.
//
struct recoverable: exception
@@ -3729,6 +3735,12 @@ namespace odb
<code>not_in_session</code>) are thrown by the <code>odb::session</code>
class and are discussed in <a href="#10">Chapter 10, "Session"</a>.</p>
+ <p>The <code>session_required</code> exception is thrown when ODB detects
+ that correctly loading a bidirectional object relationship requires a
+ session but one is not used. See <a href="#6.2">Section 6.2,
+ "Bidirectional Relationships"</a> for more information on this
+ exception.</p>
+
<p>The <code>recoverable</code> exception serves as a common base
for all the recoverable exceptions, which are: <code>connection_lost</code>,
<code>timeout</code>, and <code>deadlock</code>. The
@@ -5143,6 +5155,50 @@ private:
};
</pre>
+ <p>At the beginning of this chapter we examined how to use a session
+ to make sure a single object is shared among all other objects pointing
+ to it. With bidirectional relationships involving weak pointers the
+ use of a session becomes even more crucial. Consider the following
+ transaction that tries to load the <code>position</code> object
+ from the above example without using a session:</p>
+
+ <pre class="c++">
+transaction t (db.begin ())
+shared_ptr&lt;position> p (db.load&lt;position> (1));
+...
+t.commit ();
+ </pre>
+
+ <p>When we load the <code>position</code> object, the <code>employee</code>
+ object, which it points to, is also loaded. While <code>employee</code>
+ is initially stored as <code>shared_ptr</code>, it is then assigned to
+ the <code>employee_</code> member which is <code>weak_ptr</code>. Once
+ the assignment is complete, the shared pointer goes out of scope
+ and the only pointer that points to the newly loaded
+ <code>employee</code> object is the <code>employee_</code> weak
+ pointer. And that means the <code>employee</code> object is deleted
+ immediately after being loaded. To help avoid such pathological
+ situations ODB detects cases where a newly loaded object will
+ immediately be deleted and throws the <code>odb::session_required</code>
+ exception.</p>
+
+ <p>As the exception name suggests, the easiest way to resolve this
+ problem is to use a session:</p>
+
+ <pre class="c++">
+session s;
+transaction t (db.begin ())
+shared_ptr&lt;position> p (db.load&lt;position> (1));
+...
+t.commit ();
+ </pre>
+
+ <p>In our example, the session will maintain a shared pointer to the
+ loaded <code>employee</code> object preventing its immediate
+ deletion. Another way to resolve this problem is to avoid
+ immediate loading of the pointed-to objects using lazy weak
+ pointers. Lazy pointers are discussed in <a href="#6.3">Section 6.3,
+ "Lazy Pointers"</a> later in this chapter.</p>
<p>Above, to model a bidirectional relationship in persistent classes,
we used two pointers, one in each object. While this is a natural
diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx
index fb13ade..a8edcf3 100644
--- a/odb/relational/source.hxx
+++ b/odb/relational/source.hxx
@@ -1195,6 +1195,7 @@ namespace relational
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
@@ -1202,6 +1203,23 @@ namespace relational
<< member << " = ptr_traits::pointer_type (" << endl
<< "db->load< obj_traits::object_type > (id));";
+ // If we are loading into an eager weak pointer, make sure there
+ // is someone else holding a strong pointer to it (normally a
+ // session). Otherwise, the object will be loaded and immediately
+ // deleted. Besides not making much sense, this also breaks the
+ // delayed loading machinery which expects the object to be around
+ // at least until the top-level load() returns.
+ //
+ if (weak_pointer (pt))
+ {
+ os << endl
+ << "if (pointer_traits< ptr_traits::strong_pointer_type >" <<
+ "::null_ptr (" << endl
+ << "ptr_traits::lock (" << member << ")))" << endl
+ << "throw session_required ();";
+ }
+ }
+
// @@ Composite value currently cannot be NULL.
//
if (!composite (mi.t))