From 1c1544f5297f88bbbcbbad2d21c4ec7b62bb9a28 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov 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 +// 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 + +#include +#include + +#include + +namespace odb +{ + namespace sqlite + { + template + struct handle_traits; + + template <> + struct handle_traits + { + static void + release (sqlite3* h) + { + if (sqlite3_close (h) == SQLITE_BUSY) + { + // Connection has outstanding prepared statements. + // + assert (false); + } + } + }; + + template <> + struct handle_traits + { + static void + release (sqlite3_stmt* h) + { + sqlite3_finalize (h); + } + }; + + template + class auto_handle + { + public: + auto_handle (H* h = 0) + : h_ (h) + { + } + + ~auto_handle () + { + if (h_ != 0) + handle_traits::release (h_); + } + + H* + get () const + { + return h_; + } + + void + reset (H* h = 0) + { + if (h_ != 0) + handle_traits::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 + +#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 #include #include +#include + #include namespace odb @@ -102,7 +104,12 @@ namespace odb private: database_type& db_; - sqlite3* handle_; + auto_handle handle_; + + // Keep statement_cache_ after handle_ so that it is destroyed before + // the connection is closed. + // + std::auto_ptr statement_cache_; // Unlock notification machinery. // @@ -120,8 +127,6 @@ namespace odb private: friend class statement; statement* statements_; - - std::auto_ptr 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 (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 #include #include +#include + #include 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 stmt_; bool active_; bool cached_; -- cgit v1.1