diff options
author | Karen Arutyunov <karen@codesynthesis.com> | 2024-01-24 19:01:49 +0300 |
---|---|---|
committer | Karen Arutyunov <karen@codesynthesis.com> | 2024-01-24 19:01:49 +0300 |
commit | ba80b1f8c8354103bb98d31e252c64254f288273 (patch) | |
tree | 6e0fe6e82a58ee0fa4f9817dfc764ac5285a2368 /libodb-sqlite/odb/sqlite/transaction-impl.cxx | |
parent | b650caf5661dec901eae39e374c2c2ebd625d152 (diff) | |
parent | e5d0186db99492a139237067bab841a5b83463af (diff) |
Merge branch 'libodb-sqlite' into multi-package
Diffstat (limited to 'libodb-sqlite/odb/sqlite/transaction-impl.cxx')
-rw-r--r-- | libodb-sqlite/odb/sqlite/transaction-impl.cxx | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/libodb-sqlite/odb/sqlite/transaction-impl.cxx b/libodb-sqlite/odb/sqlite/transaction-impl.cxx new file mode 100644 index 0000000..6485f7e --- /dev/null +++ b/libodb-sqlite/odb/sqlite/transaction-impl.cxx @@ -0,0 +1,172 @@ +// file : odb/sqlite/transaction-impl.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +#include <sqlite3.h> + +#include <odb/sqlite/database.hxx> +#include <odb/sqlite/connection.hxx> +#include <odb/sqlite/statement.hxx> +#include <odb/sqlite/transaction-impl.hxx> + +namespace odb +{ + namespace sqlite + { + transaction_impl:: + transaction_impl (database_type& db, lock l) + : odb::transaction_impl (db), lock_ (l) + { + } + + transaction_impl:: + transaction_impl (connection_ptr c, lock l) + : odb::transaction_impl (c->database (), *c), + connection_ (c), + lock_ (l) + { + } + + transaction_impl:: + ~transaction_impl () + { + } + + void transaction_impl:: + start () + { + // Grab a connection if we don't already have one. + // + if (connection_ == 0) + { + connection_ = static_cast<database_type&> (database_).connection (); + odb::transaction_impl::connection_ = connection_.get (); + } + + connection_type& mc (connection_->main_connection ()); + + switch (lock_) + { + case deferred: + { + mc.begin_statement ().execute (); + break; + } + case immediate: + { + mc.begin_immediate_statement ().execute (); + break; + } + case exclusive: + { + mc.begin_exclusive_statement ().execute (); + break; + } + } + } + + // In SQLite, when a commit fails (e.g., because of the deferred + // foreign key constraint violation), the transaction may not + // be automatically rolled back. So we have to do it ourselves. + // + struct commit_guard + { + commit_guard (connection& c): c_ (&c) {} + void release () {c_ = 0;} + + ~commit_guard () + { + if (c_ != 0 && sqlite3_get_autocommit (c_->handle ()) == 0) + { + // This is happening while another exception is active. + // + try + { + c_->rollback_statement ().execute (); + } + catch (...) {} + } + } + + private: + connection* c_; + }; + + void transaction_impl:: + commit () + { + connection_type& mc (connection_->main_connection ()); + + // Invalidate query results and reset active statements. + // + // Active statements will prevent COMMIT from completing (write + // statements) or releasing the locks (read statements). Normally, a + // statement is automatically reset on completion, however, if an + // exception is thrown, that may not happen. + // + // Note: must be done via the main connection. + // + mc.clear (); + + { + commit_guard cg (mc); + mc.commit_statement ().execute (); + cg.release (); + } + + // Release the connection. + // + connection_.reset (); + } + + void transaction_impl:: + rollback () + { + connection_type& mc (connection_->main_connection ()); + + // Invalidate query results and reset active statements (the same + // reasoning as in commit()). + // + // Note: must be done via the main connection. + // + mc.clear (); + + mc.rollback_statement ().execute (); + + // Release the connection. + // + connection_.reset (); + } + + odb::connection& transaction_impl:: + connection (odb::database* pdb) + { + if (pdb == 0) + return *connection_; + + // Pick the corresponding connection for main/attached database. + // + database_type& db (static_cast<database_type&> (*pdb)); + + assert (&db.main_database () == + &static_cast<database_type&> (database_).main_database ()); + + return db.schema ().empty () + ? connection_->main_connection () + : *static_cast<attached_connection_factory&> (*db.factory_).attached_connection_; + } + + // Store transaction tracer in the main connection. + // + void transaction_impl:: + tracer (odb::tracer* t) + { + connection_->main_connection ().transaction_tracer_ = t; + } + + odb::tracer* transaction_impl:: + tracer () const + { + return connection_->main_connection ().transaction_tracer_; + } + } +} |