2022-07-10 19:40:26 +00:00
# include "MigrationRunner.h"
2022-10-24 22:20:36 +00:00
# include "BrickByBrickFix.h"
2022-10-30 07:38:43 +00:00
# include "CDClientDatabase.h"
# include "Database.h"
# include "Game.h"
2022-07-10 19:40:26 +00:00
# include "GeneralUtils.h"
2022-10-30 07:38:43 +00:00
# include "dLogger.h"
2022-11-27 11:59:59 +00:00
# include "BinaryPathFinder.h"
2022-07-10 19:40:26 +00:00
2022-10-30 07:38:43 +00:00
# include <istream>
Migration LoadMigration ( std : : string path ) {
Migration migration { } ;
2022-11-27 11:59:59 +00:00
std : : ifstream file ( BinaryPathFinder : : GetBinaryDir ( ) / " migrations/ " / path ) ;
2022-10-30 07:38:43 +00:00
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 ;
}
2022-07-10 19:40:26 +00:00
void MigrationRunner : : RunMigrations ( ) {
2022-10-24 22:20:36 +00:00
auto * stmt = Database : : CreatePreppedStmt ( " CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP()); " ) ;
stmt - > execute ( ) ;
2022-07-28 13:39:57 +00:00
delete stmt ;
sql : : SQLString finalSQL = " " ;
2022-10-24 22:20:36 +00:00
bool runSd0Migrations = false ;
2022-12-05 15:04:59 +00:00
for ( const auto & entry : GeneralUtils : : GetSqlFileNamesFromFolder ( ( BinaryPathFinder : : GetBinaryDir ( ) / " ./migrations/dlu/ " ) . string ( ) ) ) {
2022-10-30 07:38:43 +00:00
auto migration = LoadMigration ( " dlu/ " + entry ) ;
2022-07-28 13:39:57 +00:00
if ( migration . data . empty ( ) ) {
continue ;
}
stmt = Database : : CreatePreppedStmt ( " SELECT name FROM migration_history WHERE name = ?; " ) ;
2022-11-03 03:53:45 +00:00
stmt - > setString ( 1 , migration . name . c_str ( ) ) ;
2022-10-24 22:20:36 +00:00
auto * res = stmt - > executeQuery ( ) ;
2022-07-28 13:39:57 +00:00
bool doExit = res - > next ( ) ;
delete res ;
delete stmt ;
if ( doExit ) continue ;
Game : : logger - > Log ( " MigrationRunner " , " Running migration: %s " , migration . name . c_str ( ) ) ;
2022-12-22 12:06:59 +00:00
if ( migration . name = = " dlu/5_brick_model_sd0.sql " ) {
2022-10-24 22:20:36 +00:00
runSd0Migrations = true ;
} else {
2022-11-03 03:53:45 +00:00
finalSQL . append ( migration . data . c_str ( ) ) ;
2022-10-24 22:20:36 +00:00
}
2022-07-28 13:39:57 +00:00
stmt = Database : : CreatePreppedStmt ( " INSERT INTO migration_history (name) VALUES (?); " ) ;
2022-11-03 03:53:45 +00:00
stmt - > setString ( 1 , migration . name . c_str ( ) ) ;
2022-07-28 13:39:57 +00:00
stmt - > execute ( ) ;
delete stmt ;
}
2022-10-24 22:20:36 +00:00
if ( finalSQL . empty ( ) & & ! runSd0Migrations ) {
Game : : logger - > Log ( " MigrationRunner " , " Server database is up to date. " ) ;
return ;
}
2022-07-28 13:39:57 +00:00
if ( ! finalSQL . empty ( ) ) {
2022-10-24 22:20:36 +00:00
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 ;
2022-11-03 03:53:45 +00:00
simpleStatement - > execute ( query . c_str ( ) ) ;
2022-10-24 22:20:36 +00:00
} catch ( sql : : SQLException & e ) {
Game : : logger - > Log ( " MigrationRunner " , " Encountered error running migration: %s " , e . what ( ) ) ;
}
2022-07-28 13:39:57 +00:00
}
}
2022-10-24 22:20:36 +00:00
// 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 ) ;
}
2022-07-10 19:40:26 +00:00
}
2022-10-30 07:38:43 +00:00
void MigrationRunner : : RunSQLiteMigrations ( ) {
2022-12-03 12:17:13 +00:00
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 ( ) ;
2022-10-30 07:38:43 +00:00
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 ;
2022-07-10 19:40:26 +00:00
2022-12-05 15:04:59 +00:00
for ( const auto & entry : GeneralUtils : : GetSqlFileNamesFromFolder ( ( BinaryPathFinder : : GetBinaryDir ( ) / " migrations/cdserver/ " ) . string ( ) ) ) {
2022-10-30 07:38:43 +00:00
auto migration = LoadMigration ( " cdserver/ " + entry ) ;
2022-07-10 19:40:26 +00:00
2022-10-30 07:38:43 +00:00
if ( migration . data . empty ( ) ) continue ;
2022-07-10 19:40:26 +00:00
2022-12-03 12:17:13 +00:00
// 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 = ?; " ) ;
2023-04-17 08:16:23 +00:00
cdstmt . bind ( ( int32_t ) 1 , migration . name . c_str ( ) ) ;
2022-12-03 12:17:13 +00:00
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.
2022-10-30 07:38:43 +00:00
stmt = Database : : CreatePreppedStmt ( " SELECT name FROM migration_history WHERE name = ?; " ) ;
2022-11-03 03:53:45 +00:00
stmt - > setString ( 1 , migration . name . c_str ( ) ) ;
2022-10-30 07:38:43 +00:00
auto * res = stmt - > executeQuery ( ) ;
2022-12-03 12:17:13 +00:00
doExit = res - > next ( ) ;
2022-10-30 07:38:43 +00:00
delete res ;
delete stmt ;
2022-12-03 12:17:13 +00:00
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 (?); " ) ;
2023-04-17 08:16:23 +00:00
cdstmt . bind ( ( int32_t ) 1 , migration . name . c_str ( ) ) ;
2022-12-03 12:17:13 +00:00
cdstmt . execQuery ( ) . finalize ( ) ;
cdstmt . finalize ( ) ;
continue ;
}
2022-07-10 19:40:26 +00:00
2022-10-30 07:38:43 +00:00
// 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 ( ) ) ;
2022-12-05 08:57:58 +00:00
CDClientDatabase : : ExecuteQuery ( " BEGIN TRANSACTION; " ) ;
2022-10-30 07:38:43 +00:00
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 ( ) ) ;
}
}
2022-12-03 12:17:13 +00:00
// Insert into cdclient database.
cdstmt = CDClientDatabase : : CreatePreppedStmt ( " INSERT INTO migration_history (name) VALUES (?); " ) ;
2023-04-17 08:16:23 +00:00
cdstmt . bind ( ( int32_t ) 1 , migration . name . c_str ( ) ) ;
2022-12-03 12:17:13 +00:00
cdstmt . execQuery ( ) . finalize ( ) ;
cdstmt . finalize ( ) ;
2022-12-05 08:57:58 +00:00
CDClientDatabase : : ExecuteQuery ( " COMMIT; " ) ;
2022-07-28 13:39:57 +00:00
}
2022-12-03 12:17:13 +00:00
2022-10-30 07:38:43 +00:00
Game : : logger - > Log ( " MigrationRunner " , " CDServer database is up to date. " ) ;
2022-07-10 19:40:26 +00:00
}