From 7939f7972cf22ee9a74518978e4f7d4d77535e09 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 24 Mar 2011 14:02:16 +0200 Subject: Add support for clearing connection from active and uncached statements --- odb/sqlite/connection.cxx | 32 +++++++-- odb/sqlite/connection.hxx | 14 ++++ odb/sqlite/container-statements.hxx | 12 ++++ odb/sqlite/object-statements.hxx | 16 +++++ odb/sqlite/statement-cache.cxx | 3 + odb/sqlite/statement.cxx | 57 ++++++--------- odb/sqlite/statement.hxx | 140 +++++++++++++++++++++++++++++++++++- odb/sqlite/transaction-impl.cxx | 13 ++++ 8 files changed, 243 insertions(+), 44 deletions(-) (limited to 'odb/sqlite') diff --git a/odb/sqlite/connection.cxx b/odb/sqlite/connection.cxx index 37f5e20..9af5a3b 100644 --- a/odb/sqlite/connection.cxx +++ b/odb/sqlite/connection.cxx @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -19,8 +20,17 @@ 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) - : db_ (db) + : db_ (db), statements_ (0) { int f (db.flags ()); const string& n (db.name ()); @@ -47,13 +57,21 @@ namespace odb statement_cache_.reset (new statement_cache_type (*this)); } - connection:: - ~connection () + void connection:: + clear () { - statement_cache_.reset (); // Free prepared statements. - - if (sqlite3_close (handle_) == SQLITE_BUSY) - assert (false); // Connection has outstanding prepared statements. + // The current first statement will remove itself from the list + // and make the second statement (if any) the new first. + // + while (statement* s = statements_) + { + if (!s->cached ()) + s->finilize (); + else if (s->active ()) + s->reset (); + else + assert (false); // Statement is neither active nor unached. + } } } } diff --git a/odb/sqlite/connection.hxx b/odb/sqlite/connection.hxx index 0df08b3..5390609 100644 --- a/odb/sqlite/connection.hxx +++ b/odb/sqlite/connection.hxx @@ -23,6 +23,7 @@ namespace odb { namespace sqlite { + class statement; class statement_cache; class LIBODB_SQLITE_EXPORT connection: public details::shared_base @@ -55,14 +56,27 @@ namespace odb return *statement_cache_; } + public: + // Reset active and finalize uncached statements. + // + void + clear (); + private: connection (const connection&); connection& operator= (const connection&); private: + friend class statement; + database_type& db_; sqlite3* handle_; + // Linked list of active and uncached statements currently associated + // with this connection. + // + statement* statements_; + std::auto_ptr statement_cache_; }; } diff --git a/odb/sqlite/container-statements.hxx b/odb/sqlite/container-statements.hxx index 5f5caef..9bff64c 100644 --- a/odb/sqlite/container-statements.hxx +++ b/odb/sqlite/container-statements.hxx @@ -164,10 +164,14 @@ namespace odb insert_one_statement () { if (insert_one_ == 0) + { insert_one_.reset ( new (details::shared) insert_statement_type ( conn_, traits::insert_one_statement, data_image_binding_)); + insert_one_->cached (true); + } + return *insert_one_; } @@ -175,6 +179,7 @@ namespace odb select_all_statement () { if (select_all_ == 0) + { select_all_.reset ( new (details::shared) select_statement_type ( conn_, @@ -182,6 +187,9 @@ namespace odb cond_image_binding_, data_image_binding_)); + select_all_->cached (true); + } + return *select_all_; } @@ -189,10 +197,14 @@ namespace odb delete_all_statement () { if (delete_all_ == 0) + { delete_all_.reset ( new (details::shared) delete_statement_type ( conn_, traits::delete_all_statement, cond_image_binding_)); + delete_all_->cached (true); + } + return *delete_all_; } diff --git a/odb/sqlite/object-statements.hxx b/odb/sqlite/object-statements.hxx index 68fd04e..3ab627d 100644 --- a/odb/sqlite/object-statements.hxx +++ b/odb/sqlite/object-statements.hxx @@ -246,10 +246,14 @@ namespace odb persist_statement () { if (persist_ == 0) + { persist_.reset ( new (details::shared) persist_statement_type ( conn_, object_traits::persist_statement, in_image_binding_)); + persist_->cached (true); + } + return *persist_; } @@ -257,6 +261,7 @@ namespace odb find_statement () { if (find_ == 0) + { find_.reset ( new (details::shared) find_statement_type ( conn_, @@ -264,6 +269,9 @@ namespace odb id_image_binding_, out_image_binding_)); + find_->cached (true); + } + return *find_; } @@ -271,6 +279,7 @@ namespace odb update_statement () { if (update_ == 0) + { update_.reset ( new (details::shared) update_statement_type ( conn_, @@ -278,6 +287,9 @@ namespace odb id_image_binding_, in_image_binding_)); + update_->cached (true); + } + return *update_; } @@ -285,12 +297,16 @@ namespace odb erase_statement () { if (erase_ == 0) + { erase_.reset ( new (details::shared) erase_statement_type ( conn_, object_traits::erase_statement, id_image_binding_)); + erase_->cached (true); + } + return *erase_; } diff --git a/odb/sqlite/statement-cache.cxx b/odb/sqlite/statement-cache.cxx index 66ef266..383a78e 100644 --- a/odb/sqlite/statement-cache.cxx +++ b/odb/sqlite/statement-cache.cxx @@ -21,6 +21,9 @@ namespace odb commit_ (new (shared) simple_statement (conn, "COMMIT", 7)), rollback_ (new (shared) simple_statement (conn, "ROLLBACK", 9)) { + rollback_->cached (true); + commit_->cached (true); + begin_->cached (true); } } } diff --git a/odb/sqlite/statement.cxx b/odb/sqlite/statement.cxx index fc9e780..aa87939 100644 --- a/odb/sqlite/statement.cxx +++ b/odb/sqlite/statement.cxx @@ -24,27 +24,11 @@ namespace odb statement:: ~statement () { - sqlite3_finalize (stmt_); + finilize (); } - statement:: - statement (connection& conn, const string& s) - : conn_ (conn) - { - if (int e = sqlite3_prepare_v2 ( - conn_.handle (), - s.c_str (), - static_cast (s.size () + 1), - &stmt_, - 0)) - { - translate_error (e, conn_); - } - } - - statement:: - statement (connection& conn, const char* s, std::size_t n) - : conn_ (conn) + void statement:: + init (const char* s, std::size_t n) { if (int e = sqlite3_prepare_v2 ( conn_.handle (), @@ -55,6 +39,14 @@ namespace odb { translate_error (e, conn_); } + + active_ = false; + cached_ = false; + + prev_ = 0; + next_ = this; + + list_add (); // Add to the list because we are uncached. } void statement:: @@ -233,14 +225,19 @@ namespace odb void select_statement:: execute () { + if (active ()) + reset (); + done_ = false; bind_param (cond_.bind, cond_.count); + active (true); } void select_statement:: free_result () { - sqlite3_reset (stmt_); + reset (); + done_ = true; } bool select_statement:: @@ -250,23 +247,13 @@ namespace odb { int e (sqlite3_step (stmt_)); - switch (e) + if (e != SQLITE_ROW) { - case SQLITE_DONE: - { - done_ = true; - sqlite3_reset (stmt_); - break; - } - case SQLITE_ROW: - { - break; - } - default: - { - sqlite3_reset (stmt_); + reset (); + done_ = true; + + if (e != SQLITE_DONE) translate_error (e, conn_); - } } } diff --git a/odb/sqlite/statement.hxx b/odb/sqlite/statement.hxx index fe541e5..2a629fe 100644 --- a/odb/sqlite/statement.hxx +++ b/odb/sqlite/statement.hxx @@ -12,12 +12,14 @@ #include #include // std::size_t +#include #include #include #include #include +#include #include namespace odb @@ -32,10 +34,49 @@ namespace odb virtual ~statement () = 0; + sqlite3_stmt* + handle () + { + return stmt_; + } + + // Cached state (public part). + // + public: + bool + cached () const + { + return cached_; + } + + void + cached (bool cached) + { + assert (cached); + + if (!cached_) + { + if (!active_) + list_remove (); + + cached_ = true; + } + } + protected: - statement (connection&, const std::string& statement); - statement (connection&, const char* statement, std::size_t n); + statement (connection& conn, const std::string& statement) + : conn_ (conn) + { + init (statement.c_str (), statement.size () + 1); + } + + statement (connection& conn, const char* statement, std::size_t n) + : conn_ (conn) + { + init (statement, n); + } + protected: void bind_param (const bind*, std::size_t count, std::size_t start_param = 0); @@ -47,9 +88,104 @@ namespace odb bool bind_result (const bind*, std::size_t count, bool truncated = false); + // Active state. + // + protected: + bool + active () const + { + return active_; + } + + void + active (bool active) + { + assert (active); + + if (!active_) + { + list_add (); + active_ = true; + } + } + + void + reset () + { + if (active_) + { + if (stmt_ != 0) + sqlite3_reset (stmt_); + + if (cached_) + list_remove (); + + active_ = false; + } + } + + // Cached state (protected part). + // + protected: + void + finilize () + { + list_remove (); + + if (stmt_ != 0) + { + sqlite3_finalize (stmt_); + stmt_ = 0; + } + } + protected: + friend class connection; + connection& conn_; sqlite3_stmt* stmt_; + + bool active_; + bool cached_; + + private: + void + init (const char* statement, std::size_t n); + + // Doubly-linked list of active/uncached statements. + // + private: + void list_add () + { + if (next_ == this) + { + next_ = conn_.statements_; + conn_.statements_ = this; + } + } + + void list_remove () + { + if (next_ != this) + { + if (prev_ == 0) + conn_.statements_ = next_; + else + { + prev_->next_ = next_; + prev_ = 0; + } + + next_ = this; + } + } + + // prev_ == 0 means we are the first element. + // next_ == 0 means we are the last element. + // next_ == this means we are not on the list (prev_ should be 0). + // + statement* prev_; + statement* next_; }; class LIBODB_SQLITE_EXPORT simple_statement: public statement diff --git a/odb/sqlite/transaction-impl.cxx b/odb/sqlite/transaction-impl.cxx index 3730100..36d6c2b 100644 --- a/odb/sqlite/transaction-impl.cxx +++ b/odb/sqlite/transaction-impl.cxx @@ -28,6 +28,13 @@ namespace odb void transaction_impl:: commit () { + // Reset active and finilize uncached statements. Active statements + // will prevent COMMIT from completing (write statements) or releasing + // the locks (read statements). Finilization of uncached statements is + // needed to release the connection. + // + connection_->clear (); + connection_->statement_cache ().commit_statement ().execute (); // Release the connection. @@ -38,6 +45,12 @@ namespace odb void transaction_impl:: rollback () { + // Reset active and finilize uncached statements. Active statements + // will prevent ROLLBACK from completing. Finilization of uncached + // statements is needed to release the connection. + // + connection_->clear (); + connection_->statement_cache ().rollback_statement ().execute (); // Release the connection. -- cgit v1.1