1/**
2 * The Forgotten Server - a free and open-source MMORPG server emulator
3 * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "otpch.h"
21
22#include "configmanager.h"
23#include "databasemanager.h"
24#include "luascript.h"
25
26extern ConfigManager g_config;
27
28bool DatabaseManager::optimizeTables()
29{
30 Database& db = Database::getInstance();
31 std::ostringstream query;
32
33 query << "SELECT `TABLE_NAME` FROM `information_schema`.`TABLES` WHERE `TABLE_SCHEMA` = " << db.escapeString(g_config.getString(ConfigManager::MYSQL_DB)) << " AND `DATA_FREE` > 0";
34 DBResult_ptr result = db.storeQuery(query.str());
35 if (!result) {
36 return false;
37 }
38
39 do {
40 std::string tableName = result->getString("TABLE_NAME");
41 std::cout << "> Optimizing table " << tableName << "..." << std::flush;
42
43 query.str(std::string());
44 query << "OPTIMIZE TABLE `" << tableName << '`';
45
46 if (db.executeQuery(query.str())) {
47 std::cout << " [success]" << std::endl;
48 } else {
49 std::cout << " [failed]" << std::endl;
50 }
51 } while (result->next());
52 return true;
53}
54
55bool DatabaseManager::tableExists(const std::string& tableName)
56{
57 Database& db = Database::getInstance();
58
59 std::ostringstream query;
60 query << "SELECT `TABLE_NAME` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA` = " << db.escapeString(g_config.getString(ConfigManager::MYSQL_DB)) << " AND `TABLE_NAME` = " << db.escapeString(tableName) << " LIMIT 1";
61 return db.storeQuery(query.str()).get() != nullptr;
62}
63
64bool DatabaseManager::isDatabaseSetup()
65{
66 Database& db = Database::getInstance();
67 std::ostringstream query;
68 query << "SELECT `TABLE_NAME` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA` = " << db.escapeString(g_config.getString(ConfigManager::MYSQL_DB));
69 return db.storeQuery(query.str()).get() != nullptr;
70}
71
72int32_t DatabaseManager::getDatabaseVersion()
73{
74 if (!tableExists("server_config")) {
75 Database& db = Database::getInstance();
76 db.executeQuery("CREATE TABLE `server_config` (`config` VARCHAR(50) NOT NULL, `value` VARCHAR(256) NOT NULL DEFAULT '', UNIQUE(`config`)) ENGINE = InnoDB");
77 db.executeQuery("INSERT INTO `server_config` VALUES ('db_version', 0)");
78 return 0;
79 }
80
81 int32_t version = 0;
82 if (getDatabaseConfig("db_version", version)) {
83 return version;
84 }
85 return -1;
86}
87
88void DatabaseManager::updateDatabase()
89{
90 lua_State* L = luaL_newstate();
91 if (!L) {
92 return;
93 }
94
95 luaL_openlibs(L);
96
97#ifndef LUAJIT_VERSION
98 //bit operations for Lua, based on bitlib project release 24
99 //bit.bnot, bit.band, bit.bor, bit.bxor, bit.lshift, bit.rshift
100 luaL_register(L, "bit", LuaScriptInterface::luaBitReg);
101#endif
102
103 //db table
104 luaL_register(L, "db", LuaScriptInterface::luaDatabaseTable);
105
106 //result table
107 luaL_register(L, "result", LuaScriptInterface::luaResultTable);
108
109 int32_t version = getDatabaseVersion();
110 do {
111 std::ostringstream ss;
112 ss << "data/migrations/" << version << ".lua";
113 if (luaL_dofile(L, ss.str().c_str()) != 0) {
114 std::cout << "[Error - DatabaseManager::updateDatabase - Version: " << version << "] " << lua_tostring(L, -1) << std::endl;
115 break;
116 }
117
118 if (!LuaScriptInterface::reserveScriptEnv()) {
119 break;
120 }
121
122 lua_getglobal(L, "onUpdateDatabase");
123 if (lua_pcall(L, 0, 1, 0) != 0) {
124 LuaScriptInterface::resetScriptEnv();
125 std::cout << "[Error - DatabaseManager::updateDatabase - Version: " << version << "] " << lua_tostring(L, -1) << std::endl;
126 break;
127 }
128
129 if (!LuaScriptInterface::getBoolean(L, -1, false)) {
130 LuaScriptInterface::resetScriptEnv();
131 break;
132 }
133
134 version++;
135 std::cout << "> Database has been updated to version " << version << '.' << std::endl;
136 registerDatabaseConfig("db_version", version);
137
138 LuaScriptInterface::resetScriptEnv();
139 } while (true);
140 lua_close(L);
141}
142
143bool DatabaseManager::getDatabaseConfig(const std::string& config, int32_t& value)
144{
145 Database& db = Database::getInstance();
146 std::ostringstream query;
147 query << "SELECT `value` FROM `server_config` WHERE `config` = " << db.escapeString(config);
148
149 DBResult_ptr result = db.storeQuery(query.str());
150 if (!result) {
151 return false;
152 }
153
154 value = result->getNumber<int32_t>("value");
155 return true;
156}
157
158void DatabaseManager::registerDatabaseConfig(const std::string& config, int32_t value)
159{
160 Database& db = Database::getInstance();
161 std::ostringstream query;
162
163 int32_t tmp;
164
165 if (!getDatabaseConfig(config, tmp)) {
166 query << "INSERT INTO `server_config` VALUES (" << db.escapeString(config) << ", '" << value << "')";
167 } else {
168 query << "UPDATE `server_config` SET `value` = '" << value << "' WHERE `config` = " << db.escapeString(config);
169 }
170
171 db.executeQuery(query.str());
172}
173