summaryrefslogtreecommitdiff
path: root/libodb-sqlite/odb/sqlite/database.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libodb-sqlite/odb/sqlite/database.cxx')
-rw-r--r--libodb-sqlite/odb/sqlite/database.cxx306
1 files changed, 306 insertions, 0 deletions
diff --git a/libodb-sqlite/odb/sqlite/database.cxx b/libodb-sqlite/odb/sqlite/database.cxx
new file mode 100644
index 0000000..a7cf098
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/database.cxx
@@ -0,0 +1,306 @@
+// file : odb/sqlite/database.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifdef _WIN32
+# include <odb/details/win32/windows.hxx> // WideCharToMultiByte
+#endif
+
+#include <sqlite3.h>
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/connection-factory.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/transaction.hxx>
+#include <odb/sqlite/error.hxx>
+#include <odb/sqlite/exceptions.hxx>
+
+#include <odb/sqlite/details/options.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace sqlite
+ {
+ using odb::details::transfer_ptr;
+
+ database::
+ ~database ()
+ {
+ }
+
+ database::
+ database (const string& name,
+ int flags,
+ bool foreign_keys,
+ const string& vfs,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_sqlite),
+ name_ (name),
+ flags_ (flags),
+ foreign_keys_ (foreign_keys),
+ vfs_ (vfs),
+ factory_ (factory.transfer ())
+ {
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+#ifdef _WIN32
+ database::
+ database (const wstring& name,
+ int flags,
+ bool foreign_keys,
+ const string& vfs,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_sqlite),
+ flags_ (flags),
+ foreign_keys_ (foreign_keys),
+ vfs_ (vfs),
+ factory_ (factory.transfer ())
+ {
+ // Convert UTF-16 name to UTF-8 using the WideCharToMultiByte() Win32
+ // function.
+ //
+ int n (
+ WideCharToMultiByte (
+ CP_UTF8,
+ 0,
+ name.c_str (),
+ static_cast<int> (name.size ()),
+ 0,
+ 0,
+ 0,
+ 0));
+
+ if (n == 0)
+ throw database_exception (
+ SQLITE_CANTOPEN, SQLITE_CANTOPEN, "unable to open database file");
+
+ // This string is not shared so we are going to modify the underlying
+ // buffer directly.
+ //
+ name_.resize (static_cast<string::size_type> (n));
+
+ n = WideCharToMultiByte (
+ CP_UTF8,
+ 0,
+ name.c_str (),
+ static_cast<int> (name.size ()),
+ const_cast<char*> (name_.c_str ()),
+ n,
+ 0,
+ 0);
+
+ if (n == 0)
+ throw database_exception (
+ SQLITE_CANTOPEN, SQLITE_CANTOPEN, "unable to open database file");
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+#endif
+
+ database::
+ database (int& argc,
+ char* argv[],
+ bool erase,
+ int flags,
+ bool foreign_keys,
+ const string& vfs,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_sqlite),
+ flags_ (flags),
+ foreign_keys_ (foreign_keys),
+ vfs_ (vfs),
+ factory_ (factory.transfer ())
+ {
+ using namespace details;
+
+ try
+ {
+ cli::argv_file_scanner scan (argc, argv, "--options-file", erase);
+ options ops (scan, cli::unknown_mode::skip, cli::unknown_mode::skip);
+
+ name_ = ops.database ();
+
+ if (ops.create ())
+ flags_ |= SQLITE_OPEN_CREATE;
+
+ if (ops.read_only ())
+ flags_ = (flags_ & ~SQLITE_OPEN_READWRITE) | SQLITE_OPEN_READONLY;
+ }
+ catch (const cli::exception& e)
+ {
+ ostringstream ostr;
+ ostr << e;
+ throw cli_exception (ostr.str ());
+ }
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (const connection_ptr& conn,
+ const string& name,
+ const string& schema,
+ transfer_ptr<attached_connection_factory> factory)
+ : odb::database (id_sqlite),
+ name_ (name),
+ schema_ (schema),
+ flags_ (0),
+ factory_ (factory.transfer ())
+ {
+ assert (!schema_.empty ());
+
+ // Copy some things over from the connection's database.
+ //
+ database& db (conn->database ());
+
+ tracer_ = db.tracer_;
+ foreign_keys_ = db.foreign_keys_;
+
+ if (!factory_)
+ factory_.reset (new default_attached_connection_factory (
+ connection::main_connection (conn)));
+
+ factory_->database (*this);
+ }
+
+ void database::
+ print_usage (ostream& os)
+ {
+ details::options::print_usage (os);
+ }
+
+ transaction_impl* database::
+ begin ()
+ {
+ return new transaction_impl (*this, transaction_impl::deferred);
+ }
+
+ transaction_impl* database::
+ begin_immediate ()
+ {
+ return new transaction_impl (*this, transaction_impl::immediate);
+ }
+
+ transaction_impl* database::
+ begin_exclusive ()
+ {
+ return new transaction_impl (*this, transaction_impl::exclusive);
+ }
+
+ odb::connection* database::
+ connection_ ()
+ {
+ connection_ptr c (factory_->connect ());
+ return c.release ();
+ }
+
+ const database::schema_version_info& database::
+ load_schema_version (const string& name) const
+ {
+ schema_version_info& svi (schema_version_map_[name]);
+
+ // Construct the SELECT statement text.
+ //
+ string text ("SELECT \"version\", \"migration\" FROM ");
+
+ if (!svi.version_table.empty ())
+ text += svi.version_table; // Already quoted.
+ else if (!schema_version_table_.empty ())
+ text += schema_version_table_; // Already quoted.
+ else
+ text += "\"main\".\"schema_version\"";
+
+ text += " WHERE \"name\" = ?";
+
+ // Bind parameters and results.
+ //
+ size_t psize[1] = {name.size ()};
+ bind pbind[1] = {{bind::text,
+ const_cast<char*> (name.c_str ()),
+ &psize[0],
+ 0, 0, 0}};
+ binding param (pbind, 1);
+ param.version++;
+
+ long long migration;
+ bool rnull[2];
+ bind rbind[2] = {{bind::integer, &svi.version, 0, 0, &rnull[0], 0},
+ {bind::integer, &migration, 0, 0, &rnull[1], 0}};
+ binding result (rbind, 2);
+ result.version++;
+
+ // If we are not in transaction, SQLite will start an implicit one
+ // which suits us just fine.
+ //
+ connection_ptr cp;
+ if (!transaction::has_current ())
+ cp = factory_->connect ();
+
+ sqlite::connection& c (
+ cp != 0
+ ? *cp
+ : transaction::current ().connection (const_cast<database&> (*this)));
+
+ try
+ {
+ select_statement st (c,
+ text,
+ false, // Don't process.
+ false, // Don't optimize.
+ param,
+ result);
+ st.execute ();
+ auto_result ar (st);
+
+ switch (st.fetch ())
+ {
+ case select_statement::success:
+ {
+ svi.migration = migration != 0;
+ assert (st.fetch () == select_statement::no_data);
+ break;
+ }
+ case select_statement::no_data:
+ {
+ svi.version = 0; // No schema.
+ break;
+ }
+ case select_statement::truncated:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+ catch (const database_exception& e)
+ {
+ // Try to detect the case where there is no version table. SQLite
+ // doesn't have an extended error code for this so we have to use
+ // the error text.
+ //
+ if (e.error () == SQLITE_ERROR &&
+ e.message ().compare (0, 14, "no such table:") == 0)
+ svi.version = 0; // No schema.
+ else
+ throw;
+ }
+
+ return svi;
+ }
+ }
+}