2015-03-21 00:25:21 +01:00
/*
2017-01-01 16:21:30 +01:00
* Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/>
2015-03-21 00:25:21 +01:00
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "DBUpdater.h"
#include "Log.h"
2015-08-16 22:51:08 +01:00
#include "GitRevision.h"
2015-03-21 00:25:21 +01:00
#include "UpdateFetcher.h"
#include "DatabaseLoader.h"
#include "Config.h"
2016-02-21 15:52:42 +01:00
#include "BuiltInConfig.h"
2016-03-25 20:53:01 +01:00
#include "StartProcess.h"
2015-03-21 00:25:21 +01:00
#include <fstream>
#include <iostream>
#include <unordered_map>
2016-02-21 15:52:42 +01:00
std :: string DBUpdaterUtil :: GetCorrectedMySQLExecutable ()
2015-03-21 00:25:21 +01:00
{
2015-10-01 16:45:26 +02:00
if ( ! corrected_path (). empty ())
return corrected_path ();
2015-03-21 00:25:21 +01:00
else
2016-02-21 15:52:42 +01:00
return BuiltInConfig :: GetMySQLExecutable ();
2015-10-01 16:45:26 +02:00
}
bool DBUpdaterUtil :: CheckExecutable ()
{
2016-02-21 15:52:42 +01:00
boost :: filesystem :: path exe ( GetCorrectedMySQLExecutable ());
2015-10-01 16:45:26 +02:00
if ( ! exists ( exe ))
{
exe . clear ();
2016-03-25 20:53:01 +01:00
if ( auto path = Trinity :: SearchExecutableInPath ( "mysql" ))
2015-10-01 16:45:26 +02:00
{
2016-03-25 20:53:01 +01:00
exe = std :: move ( * path );
2015-10-01 16:45:26 +02:00
2016-03-25 20:53:01 +01:00
if ( ! exe . empty () && exists ( exe ))
{
// Correct the path to the cli
corrected_path () = absolute ( exe ). generic_string ();
return true ;
}
2015-10-01 16:45:26 +02:00
}
2016-06-01 22:10:20 +02:00
TC_LOG_FATAL ( "sql.updates" , "Didn't find any executable MySQL binary at \' %s \' or in path, correct the path in the *.conf ( \" MySQLExecutable \" )." ,
2015-10-01 16:45:26 +02:00
absolute ( exe ). generic_string (). c_str ());
return false ;
}
return true ;
}
std :: string & DBUpdaterUtil :: corrected_path ()
{
static std :: string path ;
return path ;
2015-03-21 00:25:21 +01:00
}
// Auth Database
template <>
std :: string DBUpdater < LoginDatabaseConnection >:: GetConfigEntry ()
{
return "Updates.Auth" ;
}
template <>
std :: string DBUpdater < LoginDatabaseConnection >:: GetTableName ()
{
return "Auth" ;
}
template <>
std :: string DBUpdater < LoginDatabaseConnection >:: GetBaseFile ()
{
2016-02-21 15:52:42 +01:00
return BuiltInConfig :: GetSourceDirectory () +
"/sql/base/auth_database.sql" ;
2015-03-21 00:25:21 +01:00
}
template <>
bool DBUpdater < LoginDatabaseConnection >:: IsEnabled ( uint32 const updateMask )
{
// This way silences warnings under msvc
return ( updateMask & DatabaseLoader :: DATABASE_LOGIN ) ? true : false ;
}
// World Database
template <>
std :: string DBUpdater < WorldDatabaseConnection >:: GetConfigEntry ()
{
return "Updates.World" ;
}
template <>
std :: string DBUpdater < WorldDatabaseConnection >:: GetTableName ()
{
return "World" ;
}
template <>
std :: string DBUpdater < WorldDatabaseConnection >:: GetBaseFile ()
{
2015-08-16 22:51:08 +01:00
return GitRevision :: GetFullDatabase ();
2015-03-21 00:25:21 +01:00
}
template <>
bool DBUpdater < WorldDatabaseConnection >:: IsEnabled ( uint32 const updateMask )
{
// This way silences warnings under msvc
return ( updateMask & DatabaseLoader :: DATABASE_WORLD ) ? true : false ;
}
template <>
BaseLocation DBUpdater < WorldDatabaseConnection >:: GetBaseLocationType ()
{
return LOCATION_DOWNLOAD ;
}
// Character Database
template <>
std :: string DBUpdater < CharacterDatabaseConnection >:: GetConfigEntry ()
{
return "Updates.Character" ;
}
template <>
std :: string DBUpdater < CharacterDatabaseConnection >:: GetTableName ()
{
return "Character" ;
}
template <>
std :: string DBUpdater < CharacterDatabaseConnection >:: GetBaseFile ()
{
2016-02-21 15:52:42 +01:00
return BuiltInConfig :: GetSourceDirectory () +
"/sql/base/characters_database.sql" ;
2015-03-21 00:25:21 +01:00
}
template <>
bool DBUpdater < CharacterDatabaseConnection >:: IsEnabled ( uint32 const updateMask )
{
// This way silences warnings under msvc
return ( updateMask & DatabaseLoader :: DATABASE_CHARACTER ) ? true : false ;
}
// Hotfix Database
template <>
std :: string DBUpdater < HotfixDatabaseConnection >:: GetConfigEntry ()
{
return "Updates.Hotfix" ;
}
template <>
std :: string DBUpdater < HotfixDatabaseConnection >:: GetTableName ()
{
return "Hotfixes" ;
}
template <>
std :: string DBUpdater < HotfixDatabaseConnection >:: GetBaseFile ()
{
2015-08-16 22:51:08 +01:00
return GitRevision :: GetHotfixesDatabase ();
2015-03-21 00:25:21 +01:00
}
template <>
bool DBUpdater < HotfixDatabaseConnection >:: IsEnabled ( uint32 const updateMask )
{
// This way silences warnings under msvc
return ( updateMask & DatabaseLoader :: DATABASE_HOTFIX ) ? true : false ;
}
2015-03-21 17:09:25 +00:00
template <>
BaseLocation DBUpdater < HotfixDatabaseConnection >:: GetBaseLocationType ()
{
return LOCATION_DOWNLOAD ;
}
2015-03-21 00:25:21 +01:00
// All
template < class T >
BaseLocation DBUpdater < T >:: GetBaseLocationType ()
{
return LOCATION_REPOSITORY ;
}
template < class T >
bool DBUpdater < T >:: Create ( DatabaseWorkerPool < T >& pool )
{
TC_LOG_INFO ( "sql.updates" , "Database \" %s \" does not exist, do you want to create it? [yes (default) / no]: " ,
pool . GetConnectionInfo () -> database . c_str ());
std :: string answer ;
std :: getline ( std :: cin , answer );
if ( ! answer . empty () && ! ( answer . substr ( 0 , 1 ) == "y" ))
return false ;
TC_LOG_INFO ( "sql.updates" , "Creating database \" %s \" ..." , pool . GetConnectionInfo () -> database . c_str ());
// Path of temp file
static Path const temp ( "create_table.sql" );
2016-06-01 22:10:20 +02:00
// Create temporary query to use external MySQL CLi
2015-03-21 00:25:21 +01:00
std :: ofstream file ( temp . generic_string ());
if ( ! file . is_open ())
{
TC_LOG_FATAL ( "sql.updates" , "Failed to create temporary query file \" %s \" !" , temp . generic_string (). c_str ());
return false ;
}
file << "CREATE DATABASE `" << pool . GetConnectionInfo () -> database << "` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci \n\n " ;
file . close ();
try
{
DBUpdater < T >:: ApplyFile ( pool , pool . GetConnectionInfo () -> host , pool . GetConnectionInfo () -> user , pool . GetConnectionInfo () -> password ,
pool . GetConnectionInfo () -> port_or_socket , "" , temp );
}
catch ( UpdateException & )
{
2016-09-12 00:51:06 +02:00
TC_LOG_FATAL ( "sql.updates" , "Failed to create database %s! Does the user (named in *.conf) have `CREATE`, `ALTER`, `DROP`, `INSERT` and `DELETE` privileges on the MySQL server?" , pool . GetConnectionInfo () -> database . c_str ());
2015-03-21 00:25:21 +01:00
boost :: filesystem :: remove ( temp );
return false ;
}
TC_LOG_INFO ( "sql.updates" , "Done." );
boost :: filesystem :: remove ( temp );
return true ;
}
template < class T >
bool DBUpdater < T >:: Update ( DatabaseWorkerPool < T >& pool )
{
2015-10-01 16:45:26 +02:00
if ( ! DBUpdaterUtil :: CheckExecutable ())
2015-03-21 00:25:21 +01:00
return false ;
TC_LOG_INFO ( "sql.updates" , "Updating %s database..." , DBUpdater < T >:: GetTableName (). c_str ());
2016-02-21 15:52:42 +01:00
Path const sourceDirectory ( BuiltInConfig :: GetSourceDirectory ());
2015-03-21 00:25:21 +01:00
if ( ! is_directory ( sourceDirectory ))
{
2016-09-12 00:51:06 +02:00
TC_LOG_ERROR ( "sql.updates" , "DBUpdater: The given source directory %s does not exist, change the path to the directory where your sql directory exists (for example c: \\ source \\ trinitycore). Shutting down." , sourceDirectory . generic_string (). c_str ());
2015-03-21 00:25:21 +01:00
return false ;
}
UpdateFetcher updateFetcher ( sourceDirectory , [ & ]( std :: string const & query ) { DBUpdater < T >:: Apply ( pool , query ); },
[ & ]( Path const & file ) { DBUpdater < T >:: ApplyFile ( pool , file ); },
[ & ]( std :: string const & query ) -> QueryResult { return DBUpdater < T >:: Retrieve ( pool , query ); });
2015-06-20 14:33:31 +02:00
UpdateResult result ;
2015-04-04 23:59:28 +02:00
try
{
2015-06-20 14:33:31 +02:00
result = updateFetcher . Update (
2015-04-04 23:59:28 +02:00
sConfigMgr -> GetBoolDefault ( "Updates.Redundancy" , true ),
sConfigMgr -> GetBoolDefault ( "Updates.AllowRehash" , true ),
sConfigMgr -> GetBoolDefault ( "Updates.ArchivedRedundancy" , false ),
sConfigMgr -> GetIntDefault ( "Updates.CleanDeadRefMaxCount" , 3 ));
}
catch ( UpdateException & )
{
return false ;
}
2015-03-21 00:25:21 +01:00
2015-06-20 14:33:31 +02:00
std :: string const info = Trinity :: StringFormat ( "Containing " SZFMTD " new and " SZFMTD " archived updates." ,
result . recent , result . archived );
if ( ! result . updated )
TC_LOG_INFO ( "sql.updates" , ">> %s database is up-to-date! %s" , DBUpdater < T >:: GetTableName (). c_str (), info . c_str ());
2015-03-21 00:25:21 +01:00
else
2015-06-21 18:13:01 +02:00
TC_LOG_INFO ( "sql.updates" , ">> Applied " SZFMTD " %s. %s" , result . updated , result . updated == 1 ? "query" : "queries" , info . c_str ());
2015-03-21 00:25:21 +01:00
return true ;
}
template < class T >
bool DBUpdater < T >:: Populate ( DatabaseWorkerPool < T >& pool )
{
{
QueryResult const result = Retrieve ( pool , "SHOW TABLES" );
if ( result && ( result -> GetRowCount () > 0 ))
return true ;
}
2015-10-01 16:45:26 +02:00
if ( ! DBUpdaterUtil :: CheckExecutable ())
2015-03-21 00:25:21 +01:00
return false ;
TC_LOG_INFO ( "sql.updates" , "Database %s is empty, auto populating it..." , DBUpdater < T >:: GetTableName (). c_str ());
std :: string const p = DBUpdater < T >:: GetBaseFile ();
if ( p . empty ())
{
TC_LOG_INFO ( "sql.updates" , ">> No base file provided, skipped!" );
return true ;
}
Path const base ( p );
if ( ! exists ( base ))
{
switch ( DBUpdater < T >:: GetBaseLocationType ())
{
case LOCATION_REPOSITORY :
{
2016-06-01 22:10:20 +02:00
TC_LOG_ERROR ( "sql.updates" , ">> Base file \" %s \" is missing. Try fixing it by cloning the source again." ,
2015-03-22 01:28:50 +01:00
base . generic_string (). c_str ());
2015-03-21 00:25:21 +01:00
break ;
}
case LOCATION_DOWNLOAD :
{
2017-04-18 21:38:47 +01:00
const char * filename = base . filename (). generic_string (). c_str ();
const char * workdir = boost :: filesystem :: current_path (). generic_string (). c_str ();
2016-01-04 22:51:48 +00:00
TC_LOG_ERROR ( "sql.updates" , ">> File \" %s \" is missing, download it from \" https://github.com/TrinityCore/TrinityCore/releases \" " \
2017-04-18 21:20:46 +01:00
" uncompress it and place the file \" %s \" in the directory \" %s \" ." , filename , filename , workdir );
2015-03-21 00:25:21 +01:00
break ;
}
}
return false ;
}
// Update database
TC_LOG_INFO ( "sql.updates" , ">> Applying \' %s \' ..." , base . generic_string (). c_str ());
2015-04-04 23:59:28 +02:00
try
{
ApplyFile ( pool , base );
}
catch ( UpdateException & )
{
return false ;
}
2015-03-21 00:25:21 +01:00
TC_LOG_INFO ( "sql.updates" , ">> Done!" );
return true ;
}
template < class T >
QueryResult DBUpdater < T >:: Retrieve ( DatabaseWorkerPool < T >& pool , std :: string const & query )
{
return pool . PQuery ( query . c_str ());
}
template < class T >
void DBUpdater < T >:: Apply ( DatabaseWorkerPool < T >& pool , std :: string const & query )
{
pool . DirectExecute ( query . c_str ());
}
template < class T >
void DBUpdater < T >:: ApplyFile ( DatabaseWorkerPool < T >& pool , Path const & path )
{
DBUpdater < T >:: ApplyFile ( pool , pool . GetConnectionInfo () -> host , pool . GetConnectionInfo () -> user , pool . GetConnectionInfo () -> password ,
pool . GetConnectionInfo () -> port_or_socket , pool . GetConnectionInfo () -> database , path );
}
template < class T >
void DBUpdater < T >:: ApplyFile ( DatabaseWorkerPool < T >& pool , std :: string const & host , std :: string const & user ,
std :: string const & password , std :: string const & port_or_socket , std :: string const & database , Path const & path )
{
std :: vector < std :: string > args ;
2015-09-26 19:18:05 +02:00
args . reserve ( 8 );
// args[0] represents the program name
args . push_back ( "mysql" );
2015-03-21 00:25:21 +01:00
// CLI Client connection info
args . push_back ( "-h" + host );
args . push_back ( "-u" + user );
2015-06-30 23:33:07 +02:00
if ( ! password . empty ())
args . push_back ( "-p" + password );
2015-05-27 18:45:16 +02:00
// Check if we want to connect through ip or socket (Unix only)
#ifdef _WIN32
2015-03-21 00:25:21 +01:00
args . push_back ( "-P" + port_or_socket );
2015-05-27 18:45:16 +02:00
#else
if ( ! std :: isdigit ( port_or_socket [ 0 ]))
{
2016-06-01 22:10:20 +02:00
// We can't check if host == "." here, because it is named localhost if socket option is enabled
2015-05-27 18:45:16 +02:00
args . push_back ( "-P0" );
args . push_back ( "--protocol=SOCKET" );
args . push_back ( "-S" + port_or_socket );
}
else
// generic case
args . push_back ( "-P" + port_or_socket );
#endif
2015-03-21 00:25:21 +01:00
// Set the default charset to utf8
args . push_back ( "--default-character-set=utf8" );
// Set max allowed packet to 1 GB
args . push_back ( "--max-allowed-packet=1GB" );
// Database
if ( ! database . empty ())
args . push_back ( database );
2016-03-25 20:53:01 +01:00
// Invokes a mysql process which doesn't leak credentials to logs
int const ret = Trinity :: StartProcess ( DBUpdaterUtil :: GetCorrectedMySQLExecutable (), args ,
"sql.updates" , path . generic_string (), true );
2015-03-21 00:25:21 +01:00
if ( ret != EXIT_SUCCESS )
{
TC_LOG_FATAL ( "sql.updates" , "Applying of file \' %s \' to database \' %s \' failed!" \
2016-07-23 03:37:09 +02:00
" If you are a user, please pull the latest revision from the repository. "
"Also make sure you have not applied any of the databases with your sql client. "
"You cannot use auto-update system and import sql files from TrinityCore repository with your sql client. "
"If you are a developer, please fix your sql query." ,
2015-03-21 00:25:21 +01:00
path . generic_string (). c_str (), pool . GetConnectionInfo () -> database . c_str ());
throw UpdateException ( "update failed" );
}
}
2016-03-11 15:57:26 +01:00
template class TC_DATABASE_API DBUpdater < LoginDatabaseConnection > ;
template class TC_DATABASE_API DBUpdater < WorldDatabaseConnection > ;
template class TC_DATABASE_API DBUpdater < CharacterDatabaseConnection > ;
template class TC_DATABASE_API DBUpdater < HotfixDatabaseConnection > ;