From 96d2e7ceb91f1c751285e8c52ba0b20895a8f81f Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Tue, 19 Feb 2019 08:24:43 +1100 Subject: [PATCH] Ensure crash in commit/rollback does not try twice It is possible that if during the rollback or commit command that there may be a disk error that will cause SQLite to return an error. When that happens we must not attempt to rollback again. This was seen when running multi-threaded and a bug in some code resulted in the SQLite file descripter being closed in the middle of a transaction. --- include/sqlite/transaction.hpp | 7 +++++++ src/sqlite/transaction.cpp | 18 +++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/include/sqlite/transaction.hpp b/include/sqlite/transaction.hpp index cfecbbf..8e3495a 100644 --- a/include/sqlite/transaction.hpp +++ b/include/sqlite/transaction.hpp @@ -100,10 +100,17 @@ namespace sqlite{ * \return \c true if transaction is still active, \c false otherwise */ bool isActive() const { return m_isActive; } + /** \brief Allow to check if transaction handled by this object is + * currently in the middle of a COMMIT/Rollback operation. It is used + * for the handling of exceptions during commit/rollback operations. + * \return \c true if transaction is committing/rolling back, \c false otherwise + */ + bool isEnding() const { return m_isEnding; } private: void exec(std::string const &); connection & m_con; bool m_isActive; ///< if \c true there is a transaction currently opened + bool m_isEnding; ///< if \c true there is a transaction is currently ending }; } diff --git a/src/sqlite/transaction.cpp b/src/sqlite/transaction.cpp index 994e037..3cda5dd 100644 --- a/src/sqlite/transaction.cpp +++ b/src/sqlite/transaction.cpp @@ -42,8 +42,18 @@ namespace sqlite{ } transaction::~transaction(){ + // when the transaction is stuck ending, the commit/rollback failed + // and we don't want to take further action, we must assume there is + // nothing we can do. + if (m_isEnding) { + return; + } if (m_isActive) { - rollback(); + try { + rollback(); + } catch (...) { + // We can't recover in the destructor, hope for the best and continue. + } } } @@ -61,17 +71,23 @@ namespace sqlite{ } void transaction::end(){ + m_isEnding = true; exec("END TRANSACTION"); + m_isEnding = false; m_isActive = false; } void transaction::commit(){ + m_isEnding = true; exec("COMMIT TRANSACTION"); + m_isEnding = false; m_isActive = false; } void transaction::rollback(){ + m_isEnding = true; exec("ROLLBACK TRANSACTION"); + m_isEnding = false; m_isActive = false; }