TrinityCore
DBUpdater.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "DBUpdater.h"
19#include "BuiltInConfig.h"
20#include "Config.h"
21#include "DatabaseEnv.h"
22#include "DatabaseLoader.h"
23#include "GitRevision.h"
24#include "Log.h"
25#include "QueryResult.h"
26#include "StartProcess.h"
27#include "UpdateFetcher.h"
28#include <boost/filesystem/operations.hpp>
29#include <fstream>
30#include <iostream>
31
33{
34 if (!corrected_path().empty())
35 return corrected_path();
36 else
38}
39
41{
42 boost::filesystem::path exe(GetCorrectedMySQLExecutable());
43 if (!is_regular_file(exe))
44 {
46 if (!exe.empty() && is_regular_file(exe))
47 {
48 // Correct the path to the cli
49 corrected_path() = absolute(exe).generic_string();
50 return true;
51 }
52
53 TC_LOG_FATAL("sql.updates", "Didn't find any executable MySQL binary at \'{}\' or in path, correct the path in the *.conf (\"MySQLExecutable\").",
54 absolute(exe).generic_string());
55
56 return false;
57 }
58 return true;
59}
60
62{
63 static std::string path;
64 return path;
65}
66
67// Auth Database
68template<>
70{
71 return "Updates.Auth";
72}
73
74template<>
76{
77 return "Auth";
78}
79
80template<>
82{
84 "/sql/base/auth_database.sql";
85}
86
87template<>
89{
90 // This way silences warnings under msvc
91 return (updateMask & DatabaseLoader::DATABASE_LOGIN) ? true : false;
92}
93
94// World Database
95template<>
97{
98 return "Updates.World";
99}
100
101template<>
103{
104 return "World";
105}
106
107template<>
109{
111}
112
113template<>
115{
116 // This way silences warnings under msvc
117 return (updateMask & DatabaseLoader::DATABASE_WORLD) ? true : false;
118}
119
120template<>
122{
123 return LOCATION_DOWNLOAD;
124}
125
126// Character Database
127template<>
129{
130 return "Updates.Character";
131}
132
133template<>
135{
136 return "Character";
137}
138
139template<>
141{
143 "/sql/base/characters_database.sql";
144}
145
146template<>
148{
149 // This way silences warnings under msvc
150 return (updateMask & DatabaseLoader::DATABASE_CHARACTER) ? true : false;
151}
152
153// Hotfix Database
154template<>
156{
157 return "Updates.Hotfix";
158}
159
160template<>
162{
163 return "Hotfixes";
164}
165
166template<>
168{
170}
171
172template<>
174{
175 // This way silences warnings under msvc
176 return (updateMask & DatabaseLoader::DATABASE_HOTFIX) ? true : false;
177}
178
179template<>
181{
182 return LOCATION_DOWNLOAD;
183}
184
185// All
186template<class T>
188{
189 return LOCATION_REPOSITORY;
190}
191
192template<class T>
194{
195 TC_LOG_INFO("sql.updates", "Database \"{}\" does not exist, do you want to create it? [yes (default) / no]: ",
197
198 std::string answer;
199 std::getline(std::cin, answer);
200 if (!answer.empty() && !(answer.substr(0, 1) == "y"))
201 return false;
202
203 TC_LOG_INFO("sql.updates", "Creating database \"{}\"...", pool.GetConnectionInfo()->database);
204
205 // Path of temp file
206 static Path const temp("create_table.sql");
207
208 // Create temporary query to use external MySQL CLi
209 std::ofstream file(temp.generic_string());
210 if (!file.is_open())
211 {
212 TC_LOG_FATAL("sql.updates", "Failed to create temporary query file \"{}\"!", temp.generic_string());
213 return false;
214 }
215
216 file << "CREATE DATABASE `" << pool.GetConnectionInfo()->database << "` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci\n\n";
217
218 file.close();
219
220 try
221 {
223 pool.GetConnectionInfo()->port_or_socket, "", pool.GetConnectionInfo()->ssl, temp);
224 }
225 catch (UpdateException&)
226 {
227 TC_LOG_FATAL("sql.updates", "Failed to create database {}! Does the user (named in *.conf) have `CREATE`, `ALTER`, `DROP`, `INSERT` and `DELETE` privileges on the MySQL server?", pool.GetConnectionInfo()->database);
228 boost::filesystem::remove(temp);
229 return false;
230 }
231
232 TC_LOG_INFO("sql.updates", "Done.");
233 boost::filesystem::remove(temp);
234 return true;
235}
236
237template<class T>
239{
241 return false;
242
243 TC_LOG_INFO("sql.updates", "Updating {} database...", DBUpdater<T>::GetTableName());
244
245 Path const sourceDirectory(BuiltInConfig::GetSourceDirectory());
246
247 if (!is_directory(sourceDirectory))
248 {
249 TC_LOG_ERROR("sql.updates", "DBUpdater: The given source directory {} does not exist, change the path to the directory where your sql directory exists (for example c:\\source\\trinitycore). Shutting down.", sourceDirectory.generic_string());
250 return false;
251 }
252
253 UpdateFetcher updateFetcher(sourceDirectory, [&](std::string const& query) { DBUpdater<T>::Apply(pool, query); },
254 [&](Path const& file) { DBUpdater<T>::ApplyFile(pool, file); },
255 [&](std::string const& query) -> QueryResult { return DBUpdater<T>::Retrieve(pool, query); });
256
257 UpdateResult result;
258 try
259 {
260 result = updateFetcher.Update(
261 sConfigMgr->GetBoolDefault("Updates.Redundancy", true),
262 sConfigMgr->GetBoolDefault("Updates.AllowRehash", true),
263 sConfigMgr->GetBoolDefault("Updates.ArchivedRedundancy", false),
264 sConfigMgr->GetIntDefault("Updates.CleanDeadRefMaxCount", 3));
265 }
266 catch (UpdateException&)
267 {
268 return false;
269 }
270
271 std::string const info = Trinity::StringFormat("Containing {} new and {} archived updates.",
272 result.recent, result.archived);
273
274 if (!result.updated)
275 TC_LOG_INFO("sql.updates", ">> {} database is up-to-date! {}", DBUpdater<T>::GetTableName(), info);
276 else
277 TC_LOG_INFO("sql.updates", ">> Applied {} {}. {}", result.updated, result.updated == 1 ? "query" : "queries", info);
278
279 return true;
280}
281
282template<class T>
284{
285 {
286 QueryResult const result = Retrieve(pool, "SHOW TABLES");
287 if (result && (result->GetRowCount() > 0))
288 return true;
289 }
290
292 return false;
293
294 TC_LOG_INFO("sql.updates", "Database {} is empty, auto populating it...", DBUpdater<T>::GetTableName());
295
296 std::string const p = DBUpdater<T>::GetBaseFile();
297 if (p.empty())
298 {
299 TC_LOG_INFO("sql.updates", ">> No base file provided, skipped!");
300 return true;
301 }
302
303 Path const base(p);
304 if (!exists(base))
305 {
307 {
309 {
310 TC_LOG_ERROR("sql.updates", ">> Base file \"{}\" is missing. Try fixing it by cloning the source again.",
311 base.generic_string());
312
313 break;
314 }
316 {
317 std::string const filename = base.filename().generic_string();
318 std::string const workdir = boost::filesystem::current_path().generic_string();
319 TC_LOG_ERROR("sql.updates", ">> File \"{}\" is missing, download it from \"https://github.com/TrinityCore/TrinityCore/releases\"" \
320 " uncompress it and place the file \"{}\" in the directory \"{}\".", filename, filename, workdir);
321 break;
322 }
323 }
324 return false;
325 }
326
327 // Update database
328 TC_LOG_INFO("sql.updates", ">> Applying \'{}\'...", base.generic_string());
329 try
330 {
331 ApplyFile(pool, base);
332 }
333 catch (UpdateException&)
334 {
335 return false;
336 }
337
338 TC_LOG_INFO("sql.updates", ">> Done!");
339 return true;
340}
341
342template<class T>
344{
345 return pool.Query(query.c_str());
346}
347
348template<class T>
349void DBUpdater<T>::Apply(DatabaseWorkerPool<T>& pool, std::string const& query)
350{
351 pool.DirectExecute(query.c_str());
352}
353
354template<class T>
356{
359}
360
361template<class T>
362void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& host, std::string const& user,
363 std::string const& password, std::string const& port_or_socket, std::string const& database, std::string const& ssl,
364 Path const& path)
365{
366 std::vector<std::string> args;
367 args.reserve(9);
368
369 // CLI Client connection info
370 args.emplace_back("-h" + host);
371 args.emplace_back("-u" + user);
372
373 if (!password.empty())
374 args.emplace_back("-p" + password);
375
376 // Check if we want to connect through ip or socket (Unix only)
377#ifdef _WIN32
378
379 if (host == ".")
380 args.emplace_back("--protocol=PIPE");
381 else
382 args.emplace_back("-P" + port_or_socket);
383
384#else
385
386 if (!std::isdigit(port_or_socket[0]))
387 {
388 // We can't check if host == "." here, because it is named localhost if socket option is enabled
389 args.emplace_back("-P0");
390 args.emplace_back("--protocol=SOCKET");
391 args.emplace_back("-S" + port_or_socket);
392 }
393 else
394 // generic case
395 args.emplace_back("-P" + port_or_socket);
396
397#endif
398
399 // Set the default charset to utf8
400 args.emplace_back("--default-character-set=utf8");
401
402 // Set max allowed packet to 1 GB
403 args.emplace_back("--max-allowed-packet=1GB");
404
405#if !defined(MARIADB_VERSION_ID) && MYSQL_VERSION_ID >= 80000
406
407 if (ssl == "ssl")
408 args.emplace_back("--ssl-mode=REQUIRED");
409
410#else
411
412 if (ssl == "ssl")
413 args.emplace_back("--ssl");
414
415#endif
416
417 // Execute sql file
418 args.emplace_back("-e");
419 args.emplace_back(Trinity::StringFormat("BEGIN; SOURCE {}; COMMIT;", path.generic_string()));
420
421 // Database
422 if (!database.empty())
423 args.emplace_back(database);
424
425 // Invokes a mysql process which doesn't leak credentials to logs
427 "sql.updates", "", true);
428
429 if (ret != EXIT_SUCCESS)
430 {
431 TC_LOG_FATAL("sql.updates", "Applying of file \'{}\' to database \'{}\' failed!" \
432 " If you are a user, please pull the latest revision from the repository. "
433 "Also make sure you have not applied any of the databases with your sql client. "
434 "You cannot use auto-update system and import sql files from TrinityCore repository with your sql client. "
435 "If you are a developer, please fix your sql query.",
436 path.generic_string(), pool.GetConnectionInfo()->database);
437
438 throw UpdateException("update failed");
439 }
440}
441
#define sConfigMgr
Definition: Config.h:61
BaseLocation
Definition: DBUpdater.h:49
@ LOCATION_DOWNLOAD
Definition: DBUpdater.h:51
@ LOCATION_REPOSITORY
Definition: DBUpdater.h:50
std::shared_ptr< ResultSet > QueryResult
#define TC_DATABASE_API
Definition: Define.h:111
uint32_t uint32
Definition: Define.h:142
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
#define TC_LOG_INFO(filterType__,...)
Definition: Log.h:159
#define TC_LOG_FATAL(filterType__,...)
Definition: Log.h:168
static std::string & corrected_path()
Definition: DBUpdater.cpp:61
static bool CheckExecutable()
Definition: DBUpdater.cpp:40
static std::string GetCorrectedMySQLExecutable()
Definition: DBUpdater.cpp:32
static BaseLocation GetBaseLocationType()
Definition: DBUpdater.cpp:187
static bool Update(DatabaseWorkerPool< T > &pool)
Definition: DBUpdater.cpp:238
boost::filesystem::path Path
Definition: DBUpdater.h:69
static QueryResult Retrieve(DatabaseWorkerPool< T > &pool, std::string const &query)
Definition: DBUpdater.cpp:343
static std::string GetBaseFile()
static void Apply(DatabaseWorkerPool< T > &pool, std::string const &query)
Definition: DBUpdater.cpp:349
static bool Create(DatabaseWorkerPool< T > &pool)
Definition: DBUpdater.cpp:193
static std::string GetTableName()
static void ApplyFile(DatabaseWorkerPool< T > &pool, Path const &path)
Definition: DBUpdater.cpp:355
static bool IsEnabled(uint32 const updateMask)
static bool Populate(DatabaseWorkerPool< T > &pool)
Definition: DBUpdater.cpp:283
static std::string GetConfigEntry()
QueryResult Query(char const *sql, T *connection=nullptr)
MySQLConnectionInfo const * GetConnectionInfo() const
void DirectExecute(char const *sql)
TC_COMMON_API std::string GetSourceDirectory()
TC_COMMON_API std::string GetMySQLExecutable()
TC_COMMON_API char const * GetHotfixesDatabase()
Definition: GitRevision.cpp:71
TC_COMMON_API char const * GetFullDatabase()
Definition: GitRevision.cpp:66
std::string SearchExecutableInPath(std::string const &filename)
int StartProcess(std::string const &executable, std::vector< std::string > const &args, std::string const &logger, std::string input_file, bool secure)
std::string StringFormat(FormatString< Args... > fmt, Args &&... args)
Default TC string format function.
Definition: StringFormat.h:38
std::string port_or_socket