From 1c1544f5297f88bbbcbbad2d21c4ec7b62bb9a28 Mon Sep 17 00:00:00 2001
From: Boris Kolpackov <boris@codesynthesis.com>
Date: Tue, 30 Aug 2011 16:10:02 +0200
Subject: Implement uniform handle management across all databases

Also use the auto_handle template instead of the raw handle in connection,
statement, and result classes. This removes a lot of brittle "exception
safety guarantee" code that we had in those classes.
---
 odb/sqlite/auto-handle.hxx | 103 +++++++++++++++++++++++++++++++++++++++++++++
 odb/sqlite/connection.cxx  |  20 ++++-----
 odb/sqlite/connection.hxx  |  11 +++--
 odb/sqlite/statement.cxx   |   5 ++-
 odb/sqlite/statement.hxx   |  11 ++---
 5 files changed, 129 insertions(+), 21 deletions(-)
 create mode 100644 odb/sqlite/auto-handle.hxx

diff --git a/odb/sqlite/auto-handle.hxx b/odb/sqlite/auto-handle.hxx
new file mode 100644
index 0000000..60b1ea9
--- /dev/null
+++ b/odb/sqlite/auto-handle.hxx
@@ -0,0 +1,103 @@
+// file      : odb/sqlite/auto-handle.hxx
+// author    : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2005-2011 Code Synthesis Tools CC
+// license   : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_AUTO_HANDLE_HXX
+#define ODB_SQLITE_AUTO_HANDLE_HXX
+
+#include <odb/pre.hxx>
+
+#include <cassert>
+#include <sqlite3.h>
+
+#include <odb/sqlite/version.hxx>
+
+namespace odb
+{
+  namespace sqlite
+  {
+    template <typename H>
+    struct handle_traits;
+
+    template <>
+    struct handle_traits<sqlite3>
+    {
+      static void
+      release (sqlite3* h)
+      {
+        if (sqlite3_close (h) == SQLITE_BUSY)
+        {
+          // Connection has outstanding prepared statements.
+          //
+          assert (false);
+        }
+      }
+    };
+
+    template <>
+    struct handle_traits<sqlite3_stmt>
+    {
+      static void
+      release (sqlite3_stmt* h)
+      {
+        sqlite3_finalize (h);
+      }
+    };
+
+    template <typename H>
+    class auto_handle
+    {
+    public:
+      auto_handle (H* h = 0)
+          : h_ (h)
+      {
+      }
+
+      ~auto_handle ()
+      {
+        if (h_ != 0)
+          handle_traits<H>::release (h_);
+      }
+
+      H*
+      get () const
+      {
+        return h_;
+      }
+
+      void
+      reset (H* h = 0)
+      {
+        if (h_ != 0)
+          handle_traits<H>::release (h_);
+
+        h_ = h;
+      }
+
+      H*
+      release ()
+      {
+        H* h (h_);
+        h_ = 0;
+        return h;
+      }
+
+      operator H* ()
+      {
+        return h_;
+      }
+
+    private:
+      auto_handle (const auto_handle&);
+      auto_handle& operator= (const auto_handle&);
+
+    private:
+      H* h_;
+    };
+  }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_AUTO_HANDLE_HXX
diff --git a/odb/sqlite/connection.cxx b/odb/sqlite/connection.cxx
index 68b57f9..7b16547 100644
--- a/odb/sqlite/connection.cxx
+++ b/odb/sqlite/connection.cxx
@@ -29,15 +29,6 @@ namespace odb
   namespace sqlite
   {
     connection::
-    ~connection ()
-    {
-      statement_cache_.reset (); // Free prepared statements.
-
-      if (sqlite3_close (handle_) == SQLITE_BUSY)
-        assert (false); // Connection has outstanding prepared statements.
-    }
-
-    connection::
     connection (database_type& db, int extra_flags)
         : odb::connection (db),
           db_ (db),
@@ -58,7 +49,11 @@ namespace odb
       if ((f & SQLITE_OPEN_FULLMUTEX) == 0)
         f |= SQLITE_OPEN_NOMUTEX;
 
-      if (int e = sqlite3_open_v2 (n.c_str (), &handle_, f, 0))
+      sqlite3* h (0);
+      int e (sqlite3_open_v2 (n.c_str (), &h, f, 0));
+      handle_.reset (h);
+
+      if (e != SQLITE_OK)
       {
         if (handle_ == 0)
           throw bad_alloc ();
@@ -98,6 +93,11 @@ namespace odb
       statement_cache_.reset (new statement_cache_type (*this));
     }
 
+    connection::
+    ~connection ()
+    {
+    }
+
     transaction_impl* connection::
     begin ()
     {
diff --git a/odb/sqlite/connection.hxx b/odb/sqlite/connection.hxx
index e487e6b..c1133eb 100644
--- a/odb/sqlite/connection.hxx
+++ b/odb/sqlite/connection.hxx
@@ -21,6 +21,8 @@
 #include <odb/sqlite/version.hxx>
 #include <odb/sqlite/forward.hxx>
 #include <odb/sqlite/transaction-impl.hxx>
+#include <odb/sqlite/auto-handle.hxx>
+
 #include <odb/sqlite/details/export.hxx>
 
 namespace odb
@@ -102,7 +104,12 @@ namespace odb
 
     private:
       database_type& db_;
-      sqlite3* handle_;
+      auto_handle<sqlite3> handle_;
+
+      // Keep statement_cache_ after handle_ so that it is destroyed before
+      // the connection is closed.
+      //
+      std::auto_ptr<statement_cache_type> statement_cache_;
 
       // Unlock notification machinery.
       //
@@ -120,8 +127,6 @@ namespace odb
     private:
       friend class statement;
       statement* statements_;
-
-      std::auto_ptr<statement_cache_type> statement_cache_;
     };
   }
 }
diff --git a/odb/sqlite/statement.cxx b/odb/sqlite/statement.cxx
index 0fe52f3..d0020d5 100644
--- a/odb/sqlite/statement.cxx
+++ b/odb/sqlite/statement.cxx
@@ -31,10 +31,11 @@ namespace odb
     init (const char* s, std::size_t n)
     {
       int e;
+      sqlite3_stmt* stmt (0);
       while ((e = sqlite3_prepare_v2 (conn_.handle (),
                                       s,
                                       static_cast<int> (n),
-                                      &stmt_,
+                                      &stmt,
                                       0)) == SQLITE_LOCKED)
       {
         conn_.wait ();
@@ -43,6 +44,8 @@ namespace odb
       if (e != SQLITE_OK)
         translate_error (e, conn_);
 
+      stmt_.reset (stmt);
+
       active_ = false;
       cached_ = false;
 
diff --git a/odb/sqlite/statement.hxx b/odb/sqlite/statement.hxx
index 5ddb56b..1d48cd4 100644
--- a/odb/sqlite/statement.hxx
+++ b/odb/sqlite/statement.hxx
@@ -20,6 +20,8 @@
 #include <odb/sqlite/version.hxx>
 #include <odb/sqlite/binding.hxx>
 #include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/auto-handle.hxx>
+
 #include <odb/sqlite/details/export.hxx>
 
 namespace odb
@@ -131,19 +133,14 @@ namespace odb
       finilize ()
       {
         list_remove ();
-
-        if (stmt_ != 0)
-        {
-          sqlite3_finalize (stmt_);
-          stmt_ = 0;
-        }
+        stmt_.reset ();
       }
 
     protected:
       friend class connection;
 
       connection& conn_;
-      sqlite3_stmt* stmt_;
+      auto_handle<sqlite3_stmt> stmt_;
 
       bool active_;
       bool cached_;
-- 
cgit v1.1