From e5d0186db99492a139237067bab841a5b83463af Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 24 Jan 2024 19:01:19 +0300 Subject: Turn libodb-sqlite repository into package for muti-package repository --- libodb-sqlite/odb/sqlite/connection-factory.cxx | 417 ++++++++++++++++++++++++ 1 file changed, 417 insertions(+) create mode 100644 libodb-sqlite/odb/sqlite/connection-factory.cxx (limited to 'libodb-sqlite/odb/sqlite/connection-factory.cxx') diff --git a/libodb-sqlite/odb/sqlite/connection-factory.cxx b/libodb-sqlite/odb/sqlite/connection-factory.cxx new file mode 100644 index 0000000..794c6dd --- /dev/null +++ b/libodb-sqlite/odb/sqlite/connection-factory.cxx @@ -0,0 +1,417 @@ +// file : odb/sqlite/connection-factory.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +#include + +#include + +#include +#include + +#include // LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY + +using namespace std; + +namespace odb +{ + using namespace details; + + namespace sqlite + { + // + // serial_connection_factory + // + + serial_connection_factory:: + ~serial_connection_factory () + { + // We should hold the last reference to the connection. + // + if (connection_ != 0) + assert (connection_.count () == 1); + } + + connection_ptr serial_connection_factory:: + create () + { + return connection_ptr (new (shared) connection (*this)); + } + + connection_ptr serial_connection_factory:: + connect () + { + return connection_; + } + + void serial_connection_factory:: + database (database_type& db) + { + connection_factory::database (db); + + if (!connection_) + connection_ = create (); + } + + // + // single_connection_factory + // + + single_connection_factory:: + ~single_connection_factory () + { + // If the connection is currently in use, wait for it to return to + // the factory. + // + lock l (mutex_); + } + + single_connection_factory::single_connection_ptr + single_connection_factory:: + create () + { + return single_connection_ptr (new (shared) single_connection (*this)); + } + + connection_ptr single_connection_factory:: + connect () + { + mutex_.lock (); + connection_->callback_ = &connection_->cb_; + connection_ptr r (connection_); + connection_.reset (); + return r; + } + + void single_connection_factory:: + database (database_type& db) + { + connection_factory::database (db); + + if (!connection_) + connection_ = create (); + } + + bool single_connection_factory:: + release (single_connection* c) + { + c->callback_ = 0; + connection_.reset (inc_ref (c)); + connection_->recycle (); + mutex_.unlock (); + return false; + } + + // + // single_connection_factory::single_connection + // + + single_connection_factory::single_connection:: + single_connection (single_connection_factory& f, int extra_flags) + : connection (f, extra_flags) + { + cb_.arg = this; + cb_.zero_counter = &zero_counter; + } + + single_connection_factory::single_connection:: + single_connection (single_connection_factory& f, sqlite3* handle) + : connection (f, handle) + { + cb_.arg = this; + cb_.zero_counter = &zero_counter; + } + + bool single_connection_factory::single_connection:: + zero_counter (void* arg) + { + single_connection* c (static_cast (arg)); + return static_cast (c->factory_).release (c); + } + + // + // new_connection_factory + // + + connection_ptr new_connection_factory:: + connect () + { + return connection_ptr (new (shared) connection (*this, extra_flags_)); + } + + void new_connection_factory:: + database (database_type& db) + { + bool first (db_ == 0); + + connection_factory::database (db); + + if (!first) + return; + + // Unless explicitly disabled, enable shared cache. + // +#if SQLITE_VERSION_NUMBER >= 3006018 && defined(LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY) + if ((db_->flags () & SQLITE_OPEN_PRIVATECACHE) == 0) + extra_flags_ |= SQLITE_OPEN_SHAREDCACHE; +#endif + } + + // + // connection_pool_factory + // + + connection_pool_factory::pooled_connection_ptr connection_pool_factory:: + create () + { + return pooled_connection_ptr ( + new (shared) pooled_connection (*this, extra_flags_)); + } + + connection_pool_factory:: + ~connection_pool_factory () + { + // Wait for all the connections currently in use to return to the pool. + // + lock l (mutex_); + while (in_use_ != 0) + { + waiters_++; + cond_.wait (l); + waiters_--; + } + } + + connection_ptr connection_pool_factory:: + connect () + { + lock l (mutex_); + + while (true) + { + // See if we have a spare connection. + // + if (connections_.size () != 0) + { + shared_ptr c (connections_.back ()); + connections_.pop_back (); + + c->callback_ = &c->cb_; + in_use_++; + return c; + } + + // See if we can create a new one. + // + if(max_ == 0 || in_use_ < max_) + { + shared_ptr c (create ()); + c->callback_ = &c->cb_; + in_use_++; + return c; + } + + // Wait until someone releases a connection. + // + waiters_++; + cond_.wait (l); + waiters_--; + } + } + + void connection_pool_factory:: + database (database_type& db) + { + bool first (db_ == 0); + + connection_factory::database (db); + + if (!first) + return; + + // Unless explicitly disabled, enable shared cache. + // +#if SQLITE_VERSION_NUMBER >= 3006018 && defined(LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY) + if ((db_->flags () & SQLITE_OPEN_PRIVATECACHE) == 0) + extra_flags_ |= SQLITE_OPEN_SHAREDCACHE; +#endif + + if (min_ > 0) + { + connections_.reserve (min_); + + for(size_t i (0); i < min_; ++i) + connections_.push_back (create ()); + } + } + + bool connection_pool_factory:: + release (pooled_connection* c) + { + c->callback_ = 0; + + lock l (mutex_); + + // Determine if we need to keep or free this connection. + // + bool keep (waiters_ != 0 || + min_ == 0 || + (connections_.size () + in_use_ <= min_)); + + in_use_--; + + if (keep) + { + connections_.push_back (pooled_connection_ptr (inc_ref (c))); + connections_.back ()->recycle (); + } + + if (waiters_ != 0) + cond_.signal (); + + return !keep; + } + + // + // connection_pool_factory::pooled_connection + // + + connection_pool_factory::pooled_connection:: + pooled_connection (connection_pool_factory& f, int extra_flags) + : connection (f, extra_flags) + { + cb_.arg = this; + cb_.zero_counter = &zero_counter; + } + + connection_pool_factory::pooled_connection:: + pooled_connection (connection_pool_factory& f, sqlite3* handle) + : connection (f, handle) + { + cb_.arg = this; + cb_.zero_counter = &zero_counter; + } + + bool connection_pool_factory::pooled_connection:: + zero_counter (void* arg) + { + pooled_connection* c (static_cast (arg)); + return static_cast (c->factory_).release (c); + } + + // + // default_attached_connection_factory + // + + void default_attached_connection_factory:: + detach () + { + // Note that this function may be called several times, for example, in + // case of detach_database() failure. + // + if (attached_connection_ != 0) + { + // We should hold the last reference to the attached connection. + // + assert (attached_connection_.count () == 1); + + // While it may seem like a good idea to also invalidate query results + // and reset active statements, if any such result/statement is still + // alive, then there would be bigger problems since it would have a + // dangling reference to the connection. In a way, that's the same + // reason we don't do it in the connection destructor. + + // Remove ourselves from the active object list of the main + // connection. + // + if (next_ != this) // Might have already been done. + list_remove (); + + const string& s (database ().schema ()); + + if (s != "main" && s != "temp") + main_factory ().detach_database (main_connection_, s); + + // Explicitly free the attached connection so that we don't try to + // redo this. + // + attached_connection_.reset (); + } + } + + default_attached_connection_factory:: + ~default_attached_connection_factory () + { + if (attached_connection_ != 0) + { + // This can throw. Ignoring the failure to detach seems like the most + // sensible thing to do here. + // + try{ detach (); } catch (...) {} + } + } + + connection_ptr default_attached_connection_factory:: + connect () + { + return attached_connection_; + } + + void default_attached_connection_factory:: + database (database_type& db) + { + attached_connection_factory::database (db); + + if (!attached_connection_) + { + const string& s (db.schema ()); + + if (s != "main" && s != "temp") + main_factory ().attach_database (main_connection_, db.name (), s); + + attached_connection_.reset ( + new (shared) connection (*this, + s != "main" ? &translate_statement : 0)); + + // Add ourselves to the active object list of the main connection. + // + list_add (); + } + } + + void default_attached_connection_factory:: + clear () + { + attached_connection_->clear (); + } + + void default_attached_connection_factory:: + translate_statement (string& r, + const char* text, + size_t text_size, + connection& conn) + { + r.assign (text, text_size); + + // Things will fall apart if any of the statements we translate use + // "main" as a table alias. So we have this crude check even though it + // means we cannot use "main" for other aliases (e.g., column). + // + assert (r.find ("AS \"main\"") == string::npos); + + const string& s (conn.database ().schema ()); + for (size_t p (0); (p = r.find ("\"main\".", p, 7)) != string::npos; ) + { + // Verify the preceding character. + // + if (p != 0 && r[p - 1] == '.') + { + p += 7; + continue; + } + + r.replace (p + 1, 4, s); + p += s.size () + 3; + } + } + } +} -- cgit v1.1