diff --git a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp --- a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp +++ b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp @@ -517,8 +517,8 @@ } typedef bool ShouldBeInTransaction; -typedef std::pair, ShouldBeInTransaction> - SQLiteMigration; +typedef std::function MigrateFunction; +typedef std::pair SQLiteMigration; std::vector> migrations{ {{1, {create_drafts_table, true}}, {2, {rename_threadID_to_key, true}}, @@ -536,6 +536,59 @@ {24, {add_not_null_constraint_to_drafts, true}}, {25, {add_not_null_constraint_to_metadata, true}}}}; +enum class MigrationResult { SUCCESS, FAILURE, NOT_APPLIED }; + +MigrationResult applyMigrationWithTransaction( + sqlite3 *db, + const MigrateFunction &migrate, + int index) { + sqlite3_exec(db, "BEGIN TRANSACTION;", nullptr, nullptr, nullptr); + auto db_version = get_database_version(db); + if (index <= db_version) { + sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr); + return MigrationResult::NOT_APPLIED; + } + auto rc = migrate(db); + if (!rc) { + sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr); + return MigrationResult::FAILURE; + } + auto database_version_set = set_database_version(db, index); + if (!database_version_set) { + sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr); + return MigrationResult::FAILURE; + } + sqlite3_exec(db, "END TRANSACTION;", nullptr, nullptr, nullptr); + return MigrationResult::SUCCESS; +} + +MigrationResult applyMigrationWithoutTransaction( + sqlite3 *db, + const MigrateFunction &migrate, + int index) { + auto db_version = get_database_version(db); + if (index <= db_version) { + return MigrationResult::NOT_APPLIED; + } + auto rc = migrate(db); + if (!rc) { + return MigrationResult::FAILURE; + } + sqlite3_exec(db, "BEGIN TRANSACTION;", nullptr, nullptr, nullptr); + auto inner_db_version = get_database_version(db); + if (index <= inner_db_version) { + sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr); + return MigrationResult::NOT_APPLIED; + } + auto database_version_set = set_database_version(db, index); + if (!database_version_set) { + sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr); + return MigrationResult::FAILURE; + } + sqlite3_exec(db, "END TRANSACTION;", nullptr, nullptr, nullptr); + return MigrationResult::SUCCESS; +} + void SQLiteQueryExecutor::migrate() const { validate_encryption(); @@ -554,31 +607,30 @@ Logger::log(version_msg.str()); for (const auto &[idx, migration] : migrations) { - if (idx <= db_version) { - continue; - } const auto &[applyMigration, shouldBeInTransaction] = migration; - std::stringstream migration_msg; - + MigrationResult migrationResult; if (shouldBeInTransaction) { - sqlite3_exec(db, "BEGIN TRANSACTION;", nullptr, nullptr, nullptr); + migrationResult = applyMigrationWithTransaction(db, applyMigration, idx); + } else { + migrationResult = + applyMigrationWithoutTransaction(db, applyMigration, idx); } - auto rc = applyMigration(db); - if (!rc) { + if (migrationResult == MigrationResult::NOT_APPLIED) { + continue; + } + + std::stringstream migration_msg; + if (migrationResult == MigrationResult::FAILURE) { migration_msg << "migration " << idx << " failed." << std::endl; Logger::log(migration_msg.str()); break; } - - set_database_version(db, idx); - - if (shouldBeInTransaction) { - sqlite3_exec(db, "END TRANSACTION;", nullptr, nullptr, nullptr); + if (migrationResult == MigrationResult::SUCCESS) { + migration_msg << "migration " << idx << " succeeded." << std::endl; + Logger::log(migration_msg.str()); } - migration_msg << "migration " << idx << " succeeded." << std::endl; - Logger::log(migration_msg.str()); } sqlite3_close(db);