summaryrefslogtreecommitdiff
path: root/libodb-sqlite/odb/sqlite/connection.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libodb-sqlite/odb/sqlite/connection.cxx')
-rw-r--r--libodb-sqlite/odb/sqlite/connection.cxx310
1 files changed, 310 insertions, 0 deletions
diff --git a/libodb-sqlite/odb/sqlite/connection.cxx b/libodb-sqlite/odb/sqlite/connection.cxx
new file mode 100644
index 0000000..0445163
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/connection.cxx
@@ -0,0 +1,310 @@
+// file : odb/sqlite/connection.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <new> // std::bad_alloc
+#include <string>
+#include <cassert>
+
+#include <odb/details/lock.hxx>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/transaction.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/statement-cache.hxx>
+#include <odb/sqlite/prepared-query.hxx>
+#include <odb/sqlite/error.hxx>
+#include <odb/sqlite/exceptions.hxx> // deadlock
+
+#include <odb/sqlite/details/config.hxx> // LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+
+using namespace std;
+
+extern "C" void
+odb_sqlite_connection_unlock_callback (void**, int);
+
+namespace odb
+{
+ using namespace details;
+
+ namespace sqlite
+ {
+ connection::
+ connection (connection_factory& cf,
+ int extra_flags,
+ statement_translator* st)
+ : odb::connection (cf),
+ statement_translator_ (st),
+ unlock_cond_ (unlock_mutex_),
+ active_objects_ (0)
+ {
+ database_type& db (database ());
+
+ int f (db.flags () | extra_flags);
+ const string& n (db.name ());
+
+ // If we are opening a temporary database, then add the create flag.
+ //
+ if (n.empty () || n == ":memory:")
+ f |= SQLITE_OPEN_CREATE;
+
+ // A connection can only be used by a single thread at a time. So
+ // disable locking in SQLite unless explicitly requested.
+ //
+#if defined(SQLITE_OPEN_NOMUTEX)
+ if ((f & SQLITE_OPEN_FULLMUTEX) == 0)
+ f |= SQLITE_OPEN_NOMUTEX;
+#endif
+
+ sqlite3* h (0);
+
+ // sqlite3_open_v2() was only addedin SQLite 3.5.0.
+ //
+#if SQLITE_VERSION_NUMBER >= 3005000
+ const string& vfs (db.vfs ());
+ int e (
+ sqlite3_open_v2 (
+ n.c_str (), &h, f, (vfs.empty () ? 0 : vfs.c_str ())));
+#else
+ // Readonly opening not supported in SQLite earlier than 3.5.0.
+ //
+ assert ((f & SQLITE_OPEN_READONLY) == 0);
+ int e (sqlite3_open (n.c_str (), &h));
+#endif
+
+ handle_.reset (h);
+
+ if (e != SQLITE_OK)
+ {
+ if (handle_ == 0)
+ throw bad_alloc ();
+
+ translate_error (e, *this);
+ }
+
+ init ();
+ }
+
+ connection::
+ connection (connection_factory& cf,
+ sqlite3* handle,
+ statement_translator* st)
+ : odb::connection (cf),
+ handle_ (handle),
+ statement_translator_ (st),
+ unlock_cond_ (unlock_mutex_),
+ active_objects_ (0)
+ {
+ init ();
+ }
+
+ void connection::
+ init ()
+ {
+ database_type& db (database ());
+
+ // Enable/disable foreign key constraints.
+ //
+ generic_statement st (
+ *this,
+ db.foreign_keys ()
+ ? "PRAGMA foreign_keys=ON"
+ : "PRAGMA foreign_keys=OFF",
+ db.foreign_keys () ? 22 : 23);
+ st.execute ();
+
+ // String lengths include '\0', as per the SQLite manual suggestion.
+ //
+ begin_.reset (new (shared) generic_statement (*this, "BEGIN", 6));
+ commit_.reset (new (shared) generic_statement (*this, "COMMIT", 7));
+ rollback_.reset (new (shared) generic_statement (*this, "ROLLBACK", 9));
+
+ // Create statement cache.
+ //
+ statement_cache_.reset (new statement_cache_type (*this));
+ }
+
+ connection::
+ connection (attached_connection_factory& cf, statement_translator* st)
+ : odb::connection (cf),
+ handle_ (0),
+ statement_translator_ (st),
+ unlock_cond_ (unlock_mutex_),
+ active_objects_ (0)
+ {
+ // Copy some things over from the main connection.
+ //
+ connection& main (*cf.main_connection_);
+
+ tracer_ = main.tracer_;
+
+ // Create statement cache.
+ //
+ statement_cache_.reset (new statement_cache_type (*this));
+ }
+
+ connection::
+ ~connection ()
+ {
+ // Destroy prepared query statements before freeing the connections.
+ //
+ recycle ();
+ clear_prepared_map ();
+ }
+
+ generic_statement& connection::
+ begin_statement ()
+ {
+ return static_cast<generic_statement&> (*begin_);
+ }
+
+ generic_statement& connection::
+ begin_immediate_statement ()
+ {
+ if (!begin_immediate_)
+ begin_immediate_.reset (
+ new (shared) generic_statement (*this, "BEGIN IMMEDIATE", 16));
+
+ return static_cast<generic_statement&> (*begin_immediate_);
+ }
+
+ generic_statement& connection::
+ begin_exclusive_statement ()
+ {
+ if (!begin_exclusive_)
+ begin_exclusive_.reset (
+ new (shared) generic_statement (*this, "BEGIN EXCLUSIVE", 16));
+
+ return static_cast<generic_statement&> (*begin_exclusive_);
+ }
+
+ generic_statement& connection::
+ commit_statement ()
+ {
+ return static_cast<generic_statement&> (*commit_);
+ }
+
+ generic_statement& connection::
+ rollback_statement ()
+ {
+ return static_cast<generic_statement&> (*rollback_);
+ }
+
+ transaction_impl* connection::
+ begin ()
+ {
+ return new transaction_impl (
+ connection_ptr (inc_ref (this)), transaction_impl::deferred);
+ }
+
+ transaction_impl* connection::
+ begin_immediate ()
+ {
+ return new transaction_impl (
+ connection_ptr (inc_ref (this)), transaction_impl::immediate);
+ }
+
+ transaction_impl* connection::
+ begin_exclusive ()
+ {
+ return new transaction_impl (
+ connection_ptr (inc_ref (this)), transaction_impl::exclusive);
+ }
+
+ unsigned long long connection::
+ execute (const char* s, std::size_t n)
+ {
+ generic_statement st (*this, s, n);
+ return st.execute ();
+ }
+
+ inline void
+ connection_unlock_callback (void** args, int n)
+ {
+ for (int i (0); i < n; ++i)
+ {
+ connection* c (static_cast<connection*> (args[i]));
+ details::lock l (c->unlock_mutex_);
+ c->unlocked_ = true;
+ c->unlock_cond_.signal ();
+ }
+ }
+
+ void connection::
+ wait ()
+ {
+#ifdef LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+ unlocked_ = false;
+
+ // unlock_notify() returns SQLITE_OK or SQLITE_LOCKED (deadlock).
+ //
+ int e (sqlite3_unlock_notify (handle (),
+ &odb_sqlite_connection_unlock_callback,
+ this));
+ if (e == SQLITE_LOCKED)
+ throw deadlock ();
+
+ details::lock l (unlock_mutex_);
+
+ while (!unlocked_)
+ unlock_cond_.wait (l);
+#else
+ translate_error (SQLITE_LOCKED, *this);
+#endif
+ }
+
+ void connection::
+ clear ()
+ {
+ invalidate_results ();
+
+ // The current first active_object may remove itself from the list and
+ // make the second object (if any) the new first.
+ //
+ for (active_object** pp (&active_objects_); *pp != 0; )
+ {
+ active_object* p (*pp);
+ p->clear ();
+
+ // Move to the next object if this one decided to stay on the list.
+ //
+ if (*pp == p)
+ pp = &p->next_;
+ }
+ }
+
+ // connection_factory
+ //
+ connection_factory::
+ ~connection_factory ()
+ {
+ }
+
+ void connection_factory::
+ database (database_type& db)
+ {
+ odb::connection_factory::db_ = &db;
+ db_ = &db;
+ }
+
+ void connection_factory::
+ attach_database (const connection_ptr& conn,
+ const std::string& name,
+ const std::string& schema)
+ {
+ conn->execute ("ATTACH DATABASE '" + name + "' AS \"" + schema + '"');
+ }
+
+ void connection_factory::
+ detach_database (const connection_ptr& conn, const std::string& schema)
+ {
+ conn->execute ("DETACH DATABASE \"" + schema + '"');
+ }
+ }
+}
+
+extern "C" void
+odb_sqlite_connection_unlock_callback (void** args, int n)
+{
+ odb::sqlite::connection_unlock_callback (args, n);
+}