mirror of
https://github.com/DarkflameUniverse/DarkflameServer
synced 2024-08-30 18:43:58 +00:00
906887bda9
* Add automatic migrations for CDServer Add support to automatically migrate and update CDServers with new migrations. Also adds support to simplify the setup process by simply putting the fdb in the res folder and letting the server convert it to sqlite. This reduces the amount of back and forth when setting up a server. * Remove transaction language * Add DML execution `poggers` Add a way to execute DML commands through the sqlite connection on the server. * Make DML Commands more robust On the off chance the server is shutdown before the whole migration is run, lets just not add it to our "finished list" until the whole file is done. * Update README
131 lines
4.3 KiB
C++
131 lines
4.3 KiB
C++
#include "MigrationRunner.h"
|
|
|
|
#include "BrickByBrickFix.h"
|
|
#include "CDClientDatabase.h"
|
|
#include "Database.h"
|
|
#include "Game.h"
|
|
#include "GeneralUtils.h"
|
|
#include "dLogger.h"
|
|
|
|
#include <istream>
|
|
|
|
Migration LoadMigration(std::string path) {
|
|
Migration migration{};
|
|
std::ifstream file("./migrations/" + path);
|
|
|
|
if (file.is_open()) {
|
|
std::string line;
|
|
std::string total = "";
|
|
|
|
while (std::getline(file, line)) {
|
|
total += line;
|
|
}
|
|
|
|
file.close();
|
|
|
|
migration.name = path;
|
|
migration.data = total;
|
|
}
|
|
|
|
return migration;
|
|
}
|
|
|
|
void MigrationRunner::RunMigrations() {
|
|
auto* stmt = Database::CreatePreppedStmt("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP());");
|
|
stmt->execute();
|
|
delete stmt;
|
|
|
|
sql::SQLString finalSQL = "";
|
|
bool runSd0Migrations = false;
|
|
for (const auto& entry : GeneralUtils::GetFileNamesFromFolder("./migrations/dlu/")) {
|
|
auto migration = LoadMigration("dlu/" + entry);
|
|
|
|
if (migration.data.empty()) {
|
|
continue;
|
|
}
|
|
|
|
stmt = Database::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;");
|
|
stmt->setString(1, migration.name);
|
|
auto* res = stmt->executeQuery();
|
|
bool doExit = res->next();
|
|
delete res;
|
|
delete stmt;
|
|
if (doExit) continue;
|
|
|
|
Game::logger->Log("MigrationRunner", "Running migration: %s", migration.name.c_str());
|
|
if (migration.name == "5_brick_model_sd0.sql") {
|
|
runSd0Migrations = true;
|
|
} else {
|
|
finalSQL.append(migration.data);
|
|
}
|
|
|
|
stmt = Database::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);");
|
|
stmt->setString(1, migration.name);
|
|
stmt->execute();
|
|
delete stmt;
|
|
}
|
|
|
|
if (finalSQL.empty() && !runSd0Migrations) {
|
|
Game::logger->Log("MigrationRunner", "Server database is up to date.");
|
|
return;
|
|
}
|
|
|
|
if (!finalSQL.empty()) {
|
|
auto migration = GeneralUtils::SplitString(static_cast<std::string>(finalSQL), ';');
|
|
std::unique_ptr<sql::Statement> simpleStatement(Database::CreateStmt());
|
|
for (auto& query : migration) {
|
|
try {
|
|
if (query.empty()) continue;
|
|
simpleStatement->execute(query);
|
|
} catch (sql::SQLException& e) {
|
|
Game::logger->Log("MigrationRunner", "Encountered error running migration: %s", e.what());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Do this last on the off chance none of the other migrations have been run yet.
|
|
if (runSd0Migrations) {
|
|
uint32_t numberOfUpdatedModels = BrickByBrickFix::UpdateBrickByBrickModelsToSd0();
|
|
Game::logger->Log("MasterServer", "%i models were updated from zlib to sd0.", numberOfUpdatedModels);
|
|
uint32_t numberOfTruncatedModels = BrickByBrickFix::TruncateBrokenBrickByBrickXml();
|
|
Game::logger->Log("MasterServer", "%i models were truncated from the database.", numberOfTruncatedModels);
|
|
}
|
|
}
|
|
|
|
void MigrationRunner::RunSQLiteMigrations() {
|
|
auto* stmt = Database::CreatePreppedStmt("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP());");
|
|
stmt->execute();
|
|
delete stmt;
|
|
|
|
for (const auto& entry : GeneralUtils::GetFileNamesFromFolder("./migrations/cdserver/")) {
|
|
auto migration = LoadMigration("cdserver/" + entry);
|
|
|
|
if (migration.data.empty()) continue;
|
|
|
|
stmt = Database::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;");
|
|
stmt->setString(1, migration.name);
|
|
auto* res = stmt->executeQuery();
|
|
bool doExit = res->next();
|
|
delete res;
|
|
delete stmt;
|
|
if (doExit) continue;
|
|
|
|
// Doing these 1 migration at a time since one takes a long time and some may think it is crashing.
|
|
// This will at the least guarentee that the full migration needs to be run in order to be counted as "migrated".
|
|
Game::logger->Log("MigrationRunner", "Executing migration: %s. This may take a while. Do not shut down server.", migration.name.c_str());
|
|
for (const auto& dml : GeneralUtils::SplitString(migration.data, ';')) {
|
|
if (dml.empty()) continue;
|
|
try {
|
|
CDClientDatabase::ExecuteDML(dml.c_str());
|
|
} catch (CppSQLite3Exception& e) {
|
|
Game::logger->Log("MigrationRunner", "Encountered error running DML command: (%i) : %s", e.errorCode(), e.errorMessage());
|
|
}
|
|
}
|
|
stmt = Database::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);");
|
|
stmt->setString(1, migration.name);
|
|
stmt->execute();
|
|
delete stmt;
|
|
}
|
|
Game::logger->Log("MigrationRunner", "CDServer database is up to date.");
|
|
}
|