mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Make gnc_dbi_safe_sync_all safer.
With SQLite3 and PGSql perform all of the safe_sync actions in a SQL transaction. Unfortunately MySQL commits the transaction on the first schema-altering query (CREATE_TABLE in this case) without decrementing sql_savepoint, so raising an error when we try to release the (non-existent) save point at the end of writing the tables, so we have to fall back on detecting a failed safe_sync at the next connection attempt. Add a GncDbiSqlConnection::check_and_rollback_failed_save() to restore the database after a failed safe_save; this is performed at the next connection.
This commit is contained in:
parent
c1fa1d2fde
commit
4bf3713bf0
@ -866,6 +866,49 @@ GncDbiBackend<Type>::safe_sync (QofBook* book)
|
||||
g_return_if_fail (conn != nullptr);
|
||||
g_return_if_fail (book != nullptr);
|
||||
|
||||
ENTER ("book=%p, primary=%p", book, m_book);
|
||||
if (!conn->begin_transaction())
|
||||
{
|
||||
LEAVE("Failed to obtain a transaction.");
|
||||
return;
|
||||
}
|
||||
if (!conn->table_operation (TableOpType::backup))
|
||||
{
|
||||
conn->rollback_transaction();
|
||||
LEAVE ("Failed to rename tables");
|
||||
return;
|
||||
}
|
||||
if (!conn->drop_indexes())
|
||||
{
|
||||
conn->rollback_transaction();
|
||||
LEAVE ("Failed to drop indexes");
|
||||
return;
|
||||
}
|
||||
|
||||
sync(m_book);
|
||||
if (check_error())
|
||||
{
|
||||
conn->rollback_transaction();
|
||||
LEAVE ("Failed to create new database tables");
|
||||
return;
|
||||
}
|
||||
conn->table_operation (TableOpType::drop_backup);
|
||||
conn->commit_transaction();
|
||||
LEAVE ("book=%p", m_book);
|
||||
}
|
||||
/* MySQL commits the transaction and all savepoints after the first CREATE
|
||||
* TABLE, crashing when we try to RELEASE SAVEPOINT because the savepoint
|
||||
* doesn't exist after the commit. We must run without a wrapping transaction in
|
||||
* that case.
|
||||
*/
|
||||
template <> void
|
||||
GncDbiBackend<DbType::DBI_MYSQL>::safe_sync (QofBook* book)
|
||||
{
|
||||
auto conn = dynamic_cast<GncDbiSqlConnection*>(m_conn);
|
||||
|
||||
g_return_if_fail (conn != nullptr);
|
||||
g_return_if_fail (book != nullptr);
|
||||
|
||||
ENTER ("book=%p, primary=%p", book, m_book);
|
||||
if (!conn->table_operation (TableOpType::backup))
|
||||
{
|
||||
|
@ -91,6 +91,11 @@ GncDbiSqlConnection::GncDbiSqlConnection (DbType type, QofBackend* qbe,
|
||||
{
|
||||
if (!lock_database(ignore_lock))
|
||||
throw std::runtime_error("Failed to lock database!");
|
||||
if (!check_and_rollback_failed_save())
|
||||
{
|
||||
unlock_database();
|
||||
throw std::runtime_error("A failed safe-save was detected and rolling it back failed.");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
@ -219,6 +224,15 @@ GncDbiSqlConnection::unlock_database ()
|
||||
m_qbe->set_error (ERR_BACKEND_SERVER_ERR);
|
||||
}
|
||||
|
||||
bool
|
||||
GncDbiSqlConnection::check_and_rollback_failed_save()
|
||||
{
|
||||
auto backup_tables = m_provider->get_table_list(m_conn, "%back");
|
||||
if (backup_tables.empty())
|
||||
return true;
|
||||
return table_operation(rollback);
|
||||
}
|
||||
|
||||
GncDbiSqlConnection::~GncDbiSqlConnection()
|
||||
{
|
||||
if (m_conn)
|
||||
@ -314,8 +328,8 @@ GncDbiSqlConnection::begin_transaction () noexcept
|
||||
result = dbi_conn_queryf (m_conn, "BEGIN");
|
||||
else
|
||||
{
|
||||
std::ostringstream savepoint("savepoint_");
|
||||
savepoint << m_sql_savepoint;
|
||||
std::ostringstream savepoint;
|
||||
savepoint << "savepoint_" << m_sql_savepoint;
|
||||
result = dbi_conn_queryf(m_conn, "SAVEPOINT %s",
|
||||
savepoint.str().c_str());
|
||||
}
|
||||
@ -348,8 +362,8 @@ GncDbiSqlConnection::rollback_transaction () noexcept
|
||||
result = dbi_conn_query (m_conn, "ROLLBACK");
|
||||
else
|
||||
{
|
||||
std::ostringstream savepoint("savepoint_");
|
||||
savepoint << m_sql_savepoint;
|
||||
std::ostringstream savepoint;
|
||||
savepoint << "savepoint_" << m_sql_savepoint - 1;
|
||||
result = dbi_conn_queryf(m_conn, "ROLLBACK TO SAVEPOINT %s",
|
||||
savepoint.str().c_str());
|
||||
}
|
||||
@ -381,8 +395,8 @@ GncDbiSqlConnection::commit_transaction () noexcept
|
||||
result = dbi_conn_queryf (m_conn, "COMMIT");
|
||||
else
|
||||
{
|
||||
std::ostringstream savepoint("savepoint_");
|
||||
savepoint << m_sql_savepoint;
|
||||
std::ostringstream savepoint;
|
||||
savepoint << "savepoint_" << m_sql_savepoint - 1;
|
||||
result = dbi_conn_queryf(m_conn, "RELEASE SAVEPOINT %s",
|
||||
savepoint.str().c_str());
|
||||
}
|
||||
|
@ -110,6 +110,7 @@ private:
|
||||
unsigned int m_sql_savepoint;
|
||||
bool lock_database(bool ignore_lock);
|
||||
void unlock_database();
|
||||
bool check_and_rollback_failed_save();
|
||||
|
||||
};
|
||||
|
||||
|
@ -673,9 +673,7 @@ GncSqlBackend::init_version_info() noexcept
|
||||
bool
|
||||
GncSqlBackend::reset_version_info() noexcept
|
||||
{
|
||||
bool ok = true;
|
||||
if (!m_conn->does_table_exist (VERSION_TABLE_NAME))
|
||||
ok = create_table (VERSION_TABLE_NAME, version_table);
|
||||
bool ok = create_table (VERSION_TABLE_NAME, version_table);
|
||||
m_versions.clear();
|
||||
set_table_version ("Gnucash", gnc_prefs_get_long_version ());
|
||||
set_table_version ("Gnucash-Resave", GNUCASH_RESAVE_VERSION);
|
||||
|
Loading…
Reference in New Issue
Block a user