mirror of
				https://github.com/DarkflameUniverse/DarkflameServer.git
				synced 2025-10-31 04:32:06 +00:00 
			
		
		
		
	Start moving migration stuff!
This commit is contained in:
		| @@ -1,20 +0,0 @@ | |||||||
| #pragma once |  | ||||||
|  |  | ||||||
| #include <cstdint> |  | ||||||
|  |  | ||||||
| namespace BrickByBrickFix { |  | ||||||
| 	/** |  | ||||||
| 	 * @brief Deletes all broken BrickByBrick models that have invalid XML |  | ||||||
| 	 * |  | ||||||
| 	 * @return The number of BrickByBrick models that were truncated |  | ||||||
| 	 */ |  | ||||||
| 	uint32_t TruncateBrokenBrickByBrickXml(); |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * @brief Updates all BrickByBrick models in the database to be |  | ||||||
| 	 * in the sd0 format as opposed to a zlib compressed format. |  | ||||||
| 	 * |  | ||||||
| 	 * @return The number of BrickByBrick models that were updated |  | ||||||
| 	 */ |  | ||||||
| 	uint32_t UpdateBrickByBrickModelsToSd0(); |  | ||||||
| }; |  | ||||||
| @@ -14,7 +14,6 @@ set(DCOMMON_SOURCES | |||||||
| 		"SHA512.cpp" | 		"SHA512.cpp" | ||||||
| 		"Demangler.cpp" | 		"Demangler.cpp" | ||||||
| 		"ZCompression.cpp" | 		"ZCompression.cpp" | ||||||
| 		"BrickByBrickFix.cpp" |  | ||||||
| 		"BinaryPathFinder.cpp" | 		"BinaryPathFinder.cpp" | ||||||
| 		"FdbToSqlite.cpp" | 		"FdbToSqlite.cpp" | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| set(DDATABASE_SOURCES "CDClientDatabase.cpp" | set(DDATABASE_SOURCES "CDClientDatabase.cpp" | ||||||
| 		"CDClientManager.cpp" | 		"CDClientManager.cpp" | ||||||
| 		"Database.cpp" | 		"Database.cpp") | ||||||
| 		"MigrationRunner.cpp") |  | ||||||
|  |  | ||||||
| add_subdirectory(Tables) | add_subdirectory(Tables) | ||||||
|  |  | ||||||
| @@ -15,5 +14,11 @@ foreach (file ${DDATABASE_DATABASES_SOURCES}) | |||||||
| 	set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "Databases/${file}") | 	set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "Databases/${file}") | ||||||
| endforeach() | endforeach() | ||||||
|  |  | ||||||
|  | add_subdirectory(Databases/Migrations) | ||||||
|  |  | ||||||
|  | foreach (file ${DDATABASE_DATABASES_MIGRATIONS_SOURCES}) | ||||||
|  | 	set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "Databases/Migrations/${file}") | ||||||
|  | endforeach() | ||||||
|  |  | ||||||
| add_library(dDatabase STATIC ${DDATABASE_SOURCES}) | add_library(dDatabase STATIC ${DDATABASE_SOURCES}) | ||||||
| target_link_libraries(dDatabase sqlite3 mariadbConnCpp) | target_link_libraries(dDatabase sqlite3 mariadbConnCpp) | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								dDatabase/Databases/Migrations/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								dDatabase/Databases/Migrations/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | set(DDATABASE_DATABASES_MIGRATIONS_SOURCES "MigrationManager.cpp" "MySQLMigration.cpp") | ||||||
							
								
								
									
										90
									
								
								dDatabase/Databases/Migrations/MigrationManager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								dDatabase/Databases/Migrations/MigrationManager.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | |||||||
|  | #include "MigrationManager.h" | ||||||
|  |  | ||||||
|  | #include <fstream> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | #include "dLogger.h" | ||||||
|  | #include "GeneralUtils.h" | ||||||
|  | #include "CDClientDatabase.h" | ||||||
|  | #include "BinaryPathFinder.h" | ||||||
|  |  | ||||||
|  | Migration Migration::LoadMigration(const std::string& type, const std::string& dbType, const std::string& name) { | ||||||
|  | 	Migration migration{}; | ||||||
|  | 	std::ifstream file(BinaryPathFinder::GetBinaryDir() / "migrations/" / type / dbType / name); | ||||||
|  |  | ||||||
|  | 	if (file.is_open()) { | ||||||
|  | 		std::string line; | ||||||
|  | 		std::string total = ""; | ||||||
|  |  | ||||||
|  | 		while (std::getline(file, line)) { | ||||||
|  | 			total += line; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		file.close(); | ||||||
|  |  | ||||||
|  | 		migration.name = ; | ||||||
|  | 		migration.data = total; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return migration; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void MigrationManager::RunSQLiteMigrations() { | ||||||
|  | 	auto cdstmt = CDClientDatabase::CreatePreppedStmt("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP);"); | ||||||
|  | 	cdstmt.execQuery().finalize(); | ||||||
|  | 	cdstmt.finalize(); | ||||||
|  |  | ||||||
|  | 	for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "migrations/cdserver/").string())) { | ||||||
|  | 		auto migration = Migration::LoadMigration("cdserver", "", entry); | ||||||
|  |  | ||||||
|  | 		if (migration.data.empty()) continue; | ||||||
|  |  | ||||||
|  | 		// Check if there is an entry in the migration history table on the cdclient database. | ||||||
|  | 		cdstmt = CDClientDatabase::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;"); | ||||||
|  | 		cdstmt.bind((int32_t)1, migration.name.c_str()); | ||||||
|  | 		auto cdres = cdstmt.execQuery(); | ||||||
|  | 		bool doExit = !cdres.eof(); | ||||||
|  | 		cdres.finalize(); | ||||||
|  | 		cdstmt.finalize(); | ||||||
|  |  | ||||||
|  | 		if (doExit) continue; | ||||||
|  |  | ||||||
|  | 		// Check first if there is entry in the migration history table on the main database. | ||||||
|  | 		stmt = Database::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;"); | ||||||
|  | 		stmt->setString(1, migration.name.c_str()); | ||||||
|  | 		auto* res = stmt->executeQuery(); | ||||||
|  | 		doExit = res->next(); | ||||||
|  | 		delete res; | ||||||
|  | 		delete stmt; | ||||||
|  | 		if (doExit) { | ||||||
|  | 			// Insert into cdclient database if there is an entry in the main database but not the cdclient database. | ||||||
|  | 			cdstmt = CDClientDatabase::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);"); | ||||||
|  | 			cdstmt.bind((int32_t)1, migration.name.c_str()); | ||||||
|  | 			cdstmt.execQuery().finalize(); | ||||||
|  | 			cdstmt.finalize(); | ||||||
|  | 			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()); | ||||||
|  | 		CDClientDatabase::ExecuteQuery("BEGIN TRANSACTION;"); | ||||||
|  | 		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()); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Insert into cdclient database. | ||||||
|  | 		cdstmt = CDClientDatabase::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);"); | ||||||
|  | 		cdstmt.bind((int32_t)1, migration.name.c_str()); | ||||||
|  | 		cdstmt.execQuery().finalize(); | ||||||
|  | 		cdstmt.finalize(); | ||||||
|  | 		CDClientDatabase::ExecuteQuery("COMMIT;"); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	Game::logger->Log("MigrationRunner", "CDServer database is up to date."); | ||||||
|  | } | ||||||
							
								
								
									
										17
									
								
								dDatabase/Databases/Migrations/MigrationManager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								dDatabase/Databases/Migrations/MigrationManager.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | class DatabaseBase; | ||||||
|  |  | ||||||
|  | struct Migration { | ||||||
|  | 	std::string data; | ||||||
|  | 	std::string name; | ||||||
|  |  | ||||||
|  | 	static Migration LoadMigration(const std::string& type, const std::string& dbType, const std::string& name); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class MigrationManager { | ||||||
|  | public: | ||||||
|  | 	void RunMigrations(DatabaseBase* db); | ||||||
|  | 	void RunSQLiteMigrations(); | ||||||
|  | private: | ||||||
|  | }; | ||||||
| @@ -1,35 +1,93 @@ | |||||||
| #include "BrickByBrickFix.h" | #include "MySQLMigration.h" | ||||||
| 
 | 
 | ||||||
| #include <memory> | #include <istream> | ||||||
| #include <iostream> |  | ||||||
| #include <sstream> |  | ||||||
| 
 | 
 | ||||||
|  | #include "CDClientDatabase.h" | ||||||
|  | #include "Game.h" | ||||||
|  | #include "GeneralUtils.h" | ||||||
|  | #include "dLogger.h" | ||||||
|  | #include "BinaryPathFinder.h" | ||||||
|  | #include "ZCompression.h" | ||||||
| #include "tinyxml2.h" | #include "tinyxml2.h" | ||||||
| 
 | 
 | ||||||
| #include "Database.h" | #include "../MySQL.h" | ||||||
| #include "Game.h" |  | ||||||
| #include "ZCompression.h" |  | ||||||
| #include "dLogger.h" |  | ||||||
| 
 | 
 | ||||||
| //! Forward declarations
 | void MigrationRunner::RunMigrations(DatabaseBase* db) { | ||||||
|  | 	auto database = dynamic_cast<MySQLDatabase*>(db); | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<sql::ResultSet> GetModelsFromDatabase(); | 	auto* stmt = database->CreatePreppedStmt("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP());"); | ||||||
| void WriteSd0Magic(char* input, uint32_t chunkSize); | 	stmt->execute(); | ||||||
| bool CheckSd0Magic(sql::Blob* streamToCheck); | 	delete stmt; | ||||||
| 
 | 
 | ||||||
| /**
 | 	sql::SQLString finalSQL = ""; | ||||||
|  * @brief Truncates all models with broken data from the database. | 	bool runSd0Migrations = false; | ||||||
|  * | 	for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "./migrations/dlu/mysql").string())) { | ||||||
|  * @return The number of models deleted | 		auto migration = Migration::LoadMigration("dlu", "mysql", entry); | ||||||
|  */ | 
 | ||||||
| uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() { | 		if (migration.data.empty()) { | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		stmt = database->CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;"); | ||||||
|  | 		stmt->setString(1, migration.name.c_str()); | ||||||
|  | 		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 == "dlu/5_brick_model_sd0.sql") { | ||||||
|  | 			runSd0Migrations = true; | ||||||
|  | 		} else { | ||||||
|  | 			finalSQL.append(migration.data.c_str()); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		stmt = database->CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);"); | ||||||
|  | 		stmt->setString(1, migration.name.c_str()); | ||||||
|  | 		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.c_str()); | ||||||
|  | 			} 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 = UpdateBrickByBrickModelsToSd0(); | ||||||
|  | 		Game::logger->Log("MasterServer", "%i models were updated from zlib to sd0.", numberOfUpdatedModels); | ||||||
|  | 		 | ||||||
|  | 		uint32_t numberOfTruncatedModels = TruncateBrokenBrickByBrickXml(); | ||||||
|  | 		Game::logger->Log("MasterServer", "%i models were truncated from the database.", numberOfTruncatedModels); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | uint32_t TruncateBrokenBrickByBrickXml(MySQLDatabase* database) { | ||||||
| 	uint32_t modelsTruncated{}; | 	uint32_t modelsTruncated{}; | ||||||
| 	auto modelsToTruncate = GetModelsFromDatabase(); | 	auto modelsToTruncate = GetModelsFromDatabase(database); | ||||||
| 	bool previousCommitValue = Database::GetAutoCommit(); | 	 | ||||||
| 	Database::SetAutoCommit(false); | 	bool previousCommitValue = database->GetAutoCommit(); | ||||||
|  | 	database->SetAutoCommit(false); | ||||||
|  | 	 | ||||||
| 	while (modelsToTruncate->next()) { | 	while (modelsToTruncate->next()) { | ||||||
| 		std::unique_ptr<sql::PreparedStatement> ugcModelToDelete(Database::CreatePreppedStmt("DELETE FROM ugc WHERE ugc.id = ?;")); | 		std::unique_ptr<sql::PreparedStatement> ugcModelToDelete(database->CreatePreppedStmt("DELETE FROM ugc WHERE ugc.id = ?;")); | ||||||
| 		std::unique_ptr<sql::PreparedStatement> pcModelToDelete(Database::CreatePreppedStmt("DELETE FROM properties_contents WHERE ugc_id = ?;")); | 		std::unique_ptr<sql::PreparedStatement> pcModelToDelete(database->CreatePreppedStmt("DELETE FROM properties_contents WHERE ugc_id = ?;")); | ||||||
| 		std::string completeUncompressedModel{}; | 		std::string completeUncompressedModel{}; | ||||||
| 		uint32_t chunkCount{}; | 		uint32_t chunkCount{}; | ||||||
| 		uint64_t modelId = modelsToTruncate->getInt(1); | 		uint64_t modelId = modelsToTruncate->getInt(1); | ||||||
| @@ -95,8 +153,8 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	Database::Commit(); | 	database->Commit(); | ||||||
| 	Database::SetAutoCommit(previousCommitValue); | 	database->SetAutoCommit(previousCommitValue); | ||||||
| 	return modelsTruncated; | 	return modelsTruncated; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -106,12 +164,14 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() { | |||||||
|  * |  * | ||||||
|  * @return The number of models updated to SD0 |  * @return The number of models updated to SD0 | ||||||
|  */ |  */ | ||||||
| uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() { | uint32_t UpdateBrickByBrickModelsToSd0(MySQLDatabase* database) { | ||||||
| 	uint32_t updatedModels = 0; | 	uint32_t updatedModels = 0; | ||||||
| 	auto modelsToUpdate = GetModelsFromDatabase(); | 	auto modelsToUpdate = GetModelsFromDatabase(); | ||||||
| 	auto previousAutoCommitState = Database::GetAutoCommit(); | 	 | ||||||
| 	Database::SetAutoCommit(false); | 	auto previousAutoCommitState = database->GetAutoCommit(); | ||||||
| 	std::unique_ptr<sql::PreparedStatement> insertionStatement(Database::CreatePreppedStmt("UPDATE ugc SET lxfml = ? WHERE id = ?;")); | 	database->SetAutoCommit(false); | ||||||
|  | 	 | ||||||
|  | 	std::unique_ptr<sql::PreparedStatement> insertionStatement(database->CreatePreppedStmt("UPDATE ugc SET lxfml = ? WHERE id = ?;")); | ||||||
| 	while (modelsToUpdate->next()) { | 	while (modelsToUpdate->next()) { | ||||||
| 		int64_t modelId = modelsToUpdate->getInt64(1); | 		int64_t modelId = modelsToUpdate->getInt64(1); | ||||||
| 		std::unique_ptr<sql::Blob> oldLxfml(modelsToUpdate->getBlob(2)); | 		std::unique_ptr<sql::Blob> oldLxfml(modelsToUpdate->getBlob(2)); | ||||||
| @@ -150,13 +210,14 @@ uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() { | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	Database::Commit(); | 	 | ||||||
| 	Database::SetAutoCommit(previousAutoCommitState); | 	database->Commit(); | ||||||
|  | 	database->SetAutoCommit(previousAutoCommitState); | ||||||
| 	return updatedModels; | 	return updatedModels; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<sql::ResultSet> GetModelsFromDatabase() { | std::unique_ptr<sql::ResultSet> GetModelsFromDatabase(MySQLDatabase* database) { | ||||||
| 	std::unique_ptr<sql::PreparedStatement> modelsRawDataQuery(Database::CreatePreppedStmt("SELECT id, lxfml FROM ugc;")); | 	std::unique_ptr<sql::PreparedStatement> modelsRawDataQuery(database->CreatePreppedStmt("SELECT id, lxfml FROM ugc;")); | ||||||
| 	return std::unique_ptr<sql::ResultSet>(modelsRawDataQuery->executeQuery()); | 	return std::unique_ptr<sql::ResultSet>(modelsRawDataQuery->executeQuery()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
							
								
								
									
										13
									
								
								dDatabase/Databases/Migrations/MySQLMigration.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								dDatabase/Databases/Migrations/MySQLMigration.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | #include "MigrationManager.h" | ||||||
|  |  | ||||||
|  | class MySQLMigrationManager : public MigrationManager { | ||||||
|  | public: | ||||||
|  | 	void RunMigrations(DatabaseBase* db) override; | ||||||
|  | 	void RunSQLiteMigrations() override; | ||||||
|  | private: | ||||||
|  |  | ||||||
|  | }; | ||||||
| @@ -1,158 +0,0 @@ | |||||||
| #include "MigrationRunner.h" |  | ||||||
|  |  | ||||||
| #include "BrickByBrickFix.h" |  | ||||||
| #include "CDClientDatabase.h" |  | ||||||
| #include "Database.h" |  | ||||||
| #include "Game.h" |  | ||||||
| #include "GeneralUtils.h" |  | ||||||
| #include "dLogger.h" |  | ||||||
| #include "BinaryPathFinder.h" |  | ||||||
|  |  | ||||||
| #include <istream> |  | ||||||
|  |  | ||||||
| Migration LoadMigration(std::string path) { |  | ||||||
| 	Migration migration{}; |  | ||||||
| 	std::ifstream file(BinaryPathFinder::GetBinaryDir() / "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::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "./migrations/dlu/").string())) { |  | ||||||
| 		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.c_str()); |  | ||||||
| 		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 == "dlu/5_brick_model_sd0.sql") { |  | ||||||
| 			runSd0Migrations = true; |  | ||||||
| 		} else { |  | ||||||
| 			finalSQL.append(migration.data.c_str()); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		stmt = Database::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);"); |  | ||||||
| 		stmt->setString(1, migration.name.c_str()); |  | ||||||
| 		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.c_str()); |  | ||||||
| 			} 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 cdstmt = CDClientDatabase::CreatePreppedStmt("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP);"); |  | ||||||
| 	cdstmt.execQuery().finalize(); |  | ||||||
| 	cdstmt.finalize(); |  | ||||||
|  |  | ||||||
| 	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::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "migrations/cdserver/").string())) { |  | ||||||
| 		auto migration = LoadMigration("cdserver/" + entry); |  | ||||||
|  |  | ||||||
| 		if (migration.data.empty()) continue; |  | ||||||
|  |  | ||||||
| 		// Check if there is an entry in the migration history table on the cdclient database. |  | ||||||
| 		cdstmt = CDClientDatabase::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;"); |  | ||||||
| 		cdstmt.bind((int32_t) 1, migration.name.c_str()); |  | ||||||
| 		auto cdres = cdstmt.execQuery(); |  | ||||||
| 		bool doExit = !cdres.eof(); |  | ||||||
| 		cdres.finalize(); |  | ||||||
| 		cdstmt.finalize(); |  | ||||||
|  |  | ||||||
| 		if (doExit) continue; |  | ||||||
|  |  | ||||||
| 		// Check first if there is entry in the migration history table on the main database. |  | ||||||
| 		stmt = Database::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;"); |  | ||||||
| 		stmt->setString(1, migration.name.c_str()); |  | ||||||
| 		auto* res = stmt->executeQuery(); |  | ||||||
| 		doExit = res->next(); |  | ||||||
| 		delete res; |  | ||||||
| 		delete stmt; |  | ||||||
| 		if (doExit) { |  | ||||||
| 			// Insert into cdclient database if there is an entry in the main database but not the cdclient database. |  | ||||||
| 			cdstmt = CDClientDatabase::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);"); |  | ||||||
| 			cdstmt.bind((int32_t) 1, migration.name.c_str()); |  | ||||||
| 			cdstmt.execQuery().finalize(); |  | ||||||
| 			cdstmt.finalize(); |  | ||||||
| 			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()); |  | ||||||
| 		CDClientDatabase::ExecuteQuery("BEGIN TRANSACTION;"); |  | ||||||
| 		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()); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// Insert into cdclient database. |  | ||||||
| 		cdstmt = CDClientDatabase::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);"); |  | ||||||
| 		cdstmt.bind((int32_t) 1, migration.name.c_str()); |  | ||||||
| 		cdstmt.execQuery().finalize(); |  | ||||||
| 		cdstmt.finalize(); |  | ||||||
| 		CDClientDatabase::ExecuteQuery("COMMIT;"); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	Game::logger->Log("MigrationRunner", "CDServer database is up to date."); |  | ||||||
| } |  | ||||||
| @@ -1,13 +0,0 @@ | |||||||
| #pragma once |  | ||||||
|  |  | ||||||
| #include <string> |  | ||||||
|  |  | ||||||
| struct Migration { |  | ||||||
| 	std::string data; |  | ||||||
| 	std::string name; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| namespace MigrationRunner { |  | ||||||
| 	void RunMigrations(); |  | ||||||
| 	void RunSQLiteMigrations(); |  | ||||||
| }; |  | ||||||
| @@ -27,7 +27,7 @@ Character::Character(uint32_t id, User* parentUser) { | |||||||
| 	m_ID = id; | 	m_ID = id; | ||||||
|  |  | ||||||
| 	// Load the character | 	// Load the character | ||||||
| 	auto character = Database::Connection->GetCharacterByID(m_ID); | 	auto character = Database::Connection->GetCharacterInfoByID(m_ID); | ||||||
| 	m_Name = character.Name; | 	m_Name = character.Name; | ||||||
| 	m_UnapprovedName = character.PendingName; | 	m_UnapprovedName = character.PendingName; | ||||||
| 	m_NameRejected = character.NameRejected; | 	m_NameRejected = character.NameRejected; | ||||||
| @@ -63,7 +63,7 @@ Character::~Character() { | |||||||
|  |  | ||||||
| void Character::UpdateFromDatabase() { | void Character::UpdateFromDatabase() { | ||||||
| 	// Load the character | 	// Load the character | ||||||
| 	auto character = Database::Connection->GetCharacterByID(m_ID); | 	auto character = Database::Connection->GetCharacterInfoByID(m_ID); | ||||||
| 	m_Name = character.Name; | 	m_Name = character.Name; | ||||||
| 	m_UnapprovedName = character.PendingName; | 	m_UnapprovedName = character.PendingName; | ||||||
| 	m_NameRejected = character.NameRejected; | 	m_NameRejected = character.NameRejected; | ||||||
| @@ -365,7 +365,7 @@ void Character::WriteToDatabase() { | |||||||
| 	m_XMLData = printer->CStr(); | 	m_XMLData = printer->CStr(); | ||||||
|  |  | ||||||
| 	// Save to DB | 	// Save to DB | ||||||
| 	Database::Connection->WriteCharacterXMl(m_ID, m_XMLData); | 	Database::Connection->UpdateCharacterXML(m_ID, m_XMLData); | ||||||
| } | } | ||||||
|  |  | ||||||
| void Character::SetPlayerFlag(const uint32_t flagId, const bool value) { | void Character::SetPlayerFlag(const uint32_t flagId, const bool value) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Jett
					Jett