aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2011-08-30 16:10:02 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2011-08-30 16:10:02 +0200
commit3f64781802deb7f910a37e3998cfc358b65e24e9 (patch)
treea233fb0387c35149cc5d72965db730b531822580
parent19109c06566ecc1b2a42820145c784d12ad26b27 (diff)
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.
-rw-r--r--odb/mysql/auto-handle.hxx96
-rw-r--r--odb/mysql/connection.cxx32
-rw-r--r--odb/mysql/connection.hxx12
-rw-r--r--odb/mysql/statement.cxx4
-rw-r--r--odb/mysql/statement.hxx6
5 files changed, 128 insertions, 22 deletions
diff --git a/odb/mysql/auto-handle.hxx b/odb/mysql/auto-handle.hxx
new file mode 100644
index 0000000..6950551
--- /dev/null
+++ b/odb/mysql/auto-handle.hxx
@@ -0,0 +1,96 @@
+// file : odb/mysql/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_MYSQL_AUTO_HANDLE_HXX
+#define ODB_MYSQL_AUTO_HANDLE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/mysql.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename H>
+ struct handle_traits;
+
+ template <>
+ struct handle_traits<MYSQL>
+ {
+ static void
+ release (MYSQL* h)
+ {
+ mysql_close (h);
+ }
+ };
+
+ template <>
+ struct handle_traits<MYSQL_STMT>
+ {
+ static void
+ release (MYSQL_STMT* h)
+ {
+ mysql_stmt_close (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_MYSQL_AUTO_HANDLE_HXX
diff --git a/odb/mysql/connection.cxx b/odb/mysql/connection.cxx
index e8c9903..114ea38 100644
--- a/odb/mysql/connection.cxx
+++ b/odb/mysql/connection.cxx
@@ -25,12 +25,13 @@ namespace odb
: odb::connection (db),
db_ (db),
failed_ (false),
- handle_ (&mysql_),
active_ (0)
{
- if (mysql_init (handle_) == 0)
+ if (mysql_init (&mysql_) == 0)
throw bad_alloc ();
+ handle_.reset (&mysql_);
+
if (*db_.charset () != '\0')
// Can only fail if we pass an unknown option.
//
@@ -54,16 +55,16 @@ namespace odb
// yet.
//
unsigned int e (mysql_errno (handle_));
- string sqlstate (mysql_sqlstate (handle_));
- string message (mysql_error (handle_));
- mysql_close (handle_);
if (e == CR_OUT_OF_MEMORY)
throw bad_alloc ();
- throw database_exception (e, sqlstate, message);
+ throw database_exception (
+ e, mysql_sqlstate (handle_), mysql_error (handle_));
}
+ // Do this after we have established the connection.
+ //
statement_cache_.reset (new statement_cache_type (*this));
}
@@ -73,22 +74,16 @@ namespace odb
db_ (db),
failed_ (false),
handle_ (handle),
- active_ (0)
+ active_ (0),
+ statement_cache_ (new statement_cache_type (*this))
{
- statement_cache_.reset (new statement_cache_type (*this));
}
connection::
~connection ()
{
- // Deallocate prepared statements before we close the connection.
- //
- statement_cache_.reset ();
-
if (stmt_handles_.size () > 0)
free_stmt_handles ();
-
- mysql_close (handle_);
}
transaction_impl* connection::
@@ -169,12 +164,15 @@ namespace odb
}
void connection::
- free_stmt_handle (MYSQL_STMT* stmt)
+ free_stmt_handle (auto_handle<MYSQL_STMT>& stmt)
{
if (active_ == 0)
- mysql_stmt_close (stmt);
+ stmt.reset ();
else
- stmt_handles_.push_back (stmt);
+ {
+ stmt_handles_.push_back (stmt); // May throw.
+ stmt.release ();
+ }
}
void connection::
diff --git a/odb/mysql/connection.hxx b/odb/mysql/connection.hxx
index f4462ed..3667c10 100644
--- a/odb/mysql/connection.hxx
+++ b/odb/mysql/connection.hxx
@@ -18,6 +18,7 @@
#include <odb/mysql/version.hxx>
#include <odb/mysql/forward.hxx>
#include <odb/mysql/transaction-impl.hxx>
+#include <odb/mysql/auto-handle.hxx>
#include <odb/details/shared-ptr.hxx>
@@ -124,7 +125,7 @@ namespace odb
alloc_stmt_handle ();
void
- free_stmt_handle (MYSQL_STMT*);
+ free_stmt_handle (auto_handle<MYSQL_STMT>&);
private:
connection (const connection&);
@@ -142,11 +143,18 @@ namespace odb
bool failed_;
MYSQL mysql_;
- MYSQL* handle_;
+ auto_handle<MYSQL> handle_;
statement* active_;
+
+ // Keep statement_cache_ after handle_ so that it is destroyed before
+ // the connection is closed.
+ //
std::auto_ptr<statement_cache_type> statement_cache_;
+ // List of "delayed" statement handles to be freed next time there
+ // is no active statement.
+ //
typedef std::vector<MYSQL_STMT*> stmt_handles;
stmt_handles stmt_handles_;
};
diff --git a/odb/mysql/statement.cxx b/odb/mysql/statement.cxx
index bc29ff5..3a76c4d 100644
--- a/odb/mysql/statement.cxx
+++ b/odb/mysql/statement.cxx
@@ -29,6 +29,10 @@ namespace odb
statement::
~statement ()
{
+ // Let the connection handle the release of the statement (it
+ // may delay the actual freeing if it will mess up the currently
+ // active statement).
+ //
conn_.free_stmt_handle (stmt_);
}
diff --git a/odb/mysql/statement.hxx b/odb/mysql/statement.hxx
index 06df6bc..7cc1e1f 100644
--- a/odb/mysql/statement.hxx
+++ b/odb/mysql/statement.hxx
@@ -12,12 +12,12 @@
#include <cstddef> // std::size_t
#include <odb/forward.hxx>
+#include <odb/details/shared-ptr.hxx>
#include <odb/mysql/mysql.hxx>
#include <odb/mysql/version.hxx>
#include <odb/mysql/binding.hxx>
-
-#include <odb/details/shared-ptr.hxx>
+#include <odb/mysql/auto-handle.hxx>
#include <odb/mysql/details/export.hxx>
@@ -44,7 +44,7 @@ namespace odb
protected:
connection& conn_;
- MYSQL_STMT* stmt_;
+ auto_handle<MYSQL_STMT> stmt_;
};
class LIBODB_MYSQL_EXPORT select_statement: public statement