diff --git a/src/DBConnection.cpp b/src/DBConnection.cpp index db91bd1f1..f1b54b170 100644 --- a/src/DBConnection.cpp +++ b/src/DBConnection.cpp @@ -337,6 +337,107 @@ int DBConnection::CheckpointHook(void *data, sqlite3 *db, const char *schema, in return SQLITE_OK; } +bool TransactionScope::TransactionStart(const wxString &name) +{ + char *errmsg = nullptr; + + int rc = sqlite3_exec(mConnection.DB(), + wxT("SAVEPOINT ") + name + wxT(";"), + nullptr, + nullptr, + &errmsg); + + if (errmsg) + { + mConnection.SetDBError( + XO("Failed to create savepoint:\n\n%s").Format(name) + ); + sqlite3_free(errmsg); + } + + return rc == SQLITE_OK; +} + +bool TransactionScope::TransactionCommit(const wxString &name) +{ + char *errmsg = nullptr; + + int rc = sqlite3_exec(mConnection.DB(), + wxT("RELEASE ") + name + wxT(";"), + nullptr, + nullptr, + &errmsg); + + if (errmsg) + { + mConnection.SetDBError( + XO("Failed to release savepoint:\n\n%s").Format(name) + ); + sqlite3_free(errmsg); + } + + return rc == SQLITE_OK; +} + +bool TransactionScope::TransactionRollback(const wxString &name) +{ + char *errmsg = nullptr; + + int rc = sqlite3_exec(mConnection.DB(), + wxT("ROLLBACK TO ") + name + wxT(";"), + nullptr, + nullptr, + &errmsg); + + if (errmsg) + { + mConnection.SetDBError( + XO("Failed to release savepoint:\n\n%s").Format(name) + ); + sqlite3_free(errmsg); + } + + return rc == SQLITE_OK; +} + +TransactionScope::TransactionScope( + DBConnection &connection, const char *name) +: mConnection(connection), + mName(name) +{ + mInTrans = TransactionStart(mName); + if ( !mInTrans ) + // To do, improve the message + throw SimpleMessageBoxException( XO("Database error") ); +} + +TransactionScope::~TransactionScope() +{ + if (mInTrans) + { + // Rollback AND REMOVE the transaction + // -- must do both; rolling back a savepoint only rewinds it + // without removing it, unlike the ROLLBACK command + if (!(TransactionRollback(mName) && + TransactionCommit(mName) ) ) + { + // Do not throw from a destructor! + // This has to be a no-fail cleanup that does the best that it can. + } + } +} + +bool TransactionScope::Commit() +{ + if ( !mInTrans ) + // Misuse of this class + THROW_INCONSISTENCY_EXCEPTION; + + mInTrans = !TransactionCommit(mName); + + return mInTrans; +} + ConnectionPtr::~ConnectionPtr() { wxASSERT_MSG(!mpConnection, wxT("Project file was not closed at shutdown")); diff --git a/src/DBConnection.h b/src/DBConnection.h index 0d0dc0105..131b85e8d 100644 --- a/src/DBConnection.h +++ b/src/DBConnection.h @@ -110,6 +110,28 @@ private: bool mBypass; }; +// Make a savepoint (a transaction, possibly nested) with the given name; +// roll it back at destruction time, unless an explicit Commit() happened first. +// Commit() must not be called again after one successful call. +// An exception is thrown from the constructor if the transaction cannot open. +class TransactionScope +{ +public: + TransactionScope(DBConnection &connection, const char *name); + ~TransactionScope(); + + bool Commit(); + +private: + bool TransactionStart(const wxString &name); + bool TransactionCommit(const wxString &name); + bool TransactionRollback(const wxString &name); + + DBConnection &mConnection; + bool mInTrans; + wxString mName; +}; + using Connection = std::unique_ptr; // This object attached to the project simply holds the pointer to the diff --git a/src/ProjectFileIO.cpp b/src/ProjectFileIO.cpp index 8bd5ee103..a1b367c51 100644 --- a/src/ProjectFileIO.cpp +++ b/src/ProjectFileIO.cpp @@ -423,69 +423,6 @@ void ProjectFileIO::UseConnection(Connection &&conn, const FilePath &filePath) SetFileName(filePath); } -bool TransactionScope::TransactionStart(const wxString &name) -{ - char *errmsg = nullptr; - - int rc = sqlite3_exec(mConnection.DB(), - wxT("SAVEPOINT ") + name + wxT(";"), - nullptr, - nullptr, - &errmsg); - - if (errmsg) - { - mConnection.SetDBError( - XO("Failed to create savepoint:\n\n%s").Format(name) - ); - sqlite3_free(errmsg); - } - - return rc == SQLITE_OK; -} - -bool TransactionScope::TransactionCommit(const wxString &name) -{ - char *errmsg = nullptr; - - int rc = sqlite3_exec(mConnection.DB(), - wxT("RELEASE ") + name + wxT(";"), - nullptr, - nullptr, - &errmsg); - - if (errmsg) - { - mConnection.SetDBError( - XO("Failed to release savepoint:\n\n%s").Format(name) - ); - sqlite3_free(errmsg); - } - - return rc == SQLITE_OK; -} - -bool TransactionScope::TransactionRollback(const wxString &name) -{ - char *errmsg = nullptr; - - int rc = sqlite3_exec(mConnection.DB(), - wxT("ROLLBACK TO ") + name + wxT(";"), - nullptr, - nullptr, - &errmsg); - - if (errmsg) - { - mConnection.SetDBError( - XO("Failed to release savepoint:\n\n%s").Format(name) - ); - sqlite3_free(errmsg); - } - - return rc == SQLITE_OK; -} - static int ExecCallback(void *data, int cols, char **vals, char **names) { auto &cb = *static_cast(data); @@ -2448,41 +2385,3 @@ int ProjectFileIO::get_varint(const unsigned char *ptr, int64_t *out) return 9; } - -TransactionScope::TransactionScope( - DBConnection &connection, const char *name) -: mConnection(connection), - mName(name) -{ - mInTrans = TransactionStart(mName); - if ( !mInTrans ) - // To do, improve the message - throw SimpleMessageBoxException( XO("Database error") ); -} - -TransactionScope::~TransactionScope() -{ - if (mInTrans) - { - // Rollback AND REMOVE the transaction - // -- must do both; rolling back a savepoint only rewinds it - // without removing it, unlike the ROLLBACK command - if (!(TransactionRollback(mName) && - TransactionCommit(mName) ) ) - { - // Do not throw from a destructor! - // This has to be a no-fail cleanup that does the best that it can. - } - } -} - -bool TransactionScope::Commit() -{ - if ( !mInTrans ) - // Misuse of this class - THROW_INCONSISTENCY_EXCEPTION; - - mInTrans = !TransactionCommit(mName); - - return mInTrans; -} diff --git a/src/ProjectFileIO.h b/src/ProjectFileIO.h index 62d70868b..ee95cfae2 100644 --- a/src/ProjectFileIO.h +++ b/src/ProjectFileIO.h @@ -24,7 +24,6 @@ struct sqlite3_stmt; struct sqlite3_value; class AudacityProject; -class TransactionScope; class DBConnection; class ProjectSerializer; class SqliteSampleBlock; @@ -239,28 +238,6 @@ private: bool mPrevTemporary; }; -// Make a savepoint (a transaction, possibly nested) with the given name; -// roll it back at destruction time, unless an explicit Commit() happened first. -// Commit() must not be called again after one successful call. -// An exception is thrown from the constructor if the transaction cannot open. -class TransactionScope -{ -public: - TransactionScope(DBConnection &connection, const char *name); - ~TransactionScope(); - - bool Commit(); - -private: - bool TransactionStart(const wxString &name); - bool TransactionCommit(const wxString &name); - bool TransactionRollback(const wxString &name); - - DBConnection &mConnection; - bool mInTrans; - wxString mName; -}; - class wxTopLevelWindow; // TitleRestorer restores project window titles to what they were, in its destructor. diff --git a/src/ProjectManager.cpp b/src/ProjectManager.cpp index 5bbce8707..e30c5fae5 100644 --- a/src/ProjectManager.cpp +++ b/src/ProjectManager.cpp @@ -15,6 +15,7 @@ Paul Licameli split from AudacityProject.cpp #include "AdornedRulerPanel.h" #include "AudioIO.h" #include "Clipboard.h" +#include "DBConnection.h" #include "FileNames.h" #include "Menus.h" #include "ModuleManager.h" diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index 395d66f95..ada488b92 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -27,6 +27,7 @@ #include #include "../AudioIO.h" +#include "../DBConnection.h" #include "../LabelTrack.h" #include "../Mix.h" #include "../PluginManager.h"