From aefeb72c8c9c1c4331ef64dd5a2e156115b3d9e4 Mon Sep 17 00:00:00 2001 From: insunaa Date: Sat, 6 Jan 2024 13:59:36 +0100 Subject: [PATCH] SQLite: Add SQLite DB backend support --- CMakeLists.txt | 4 + src/game/Accounts/AccountMgr.cpp | 5 +- src/game/BattleGround/BattleGround.cpp | 2 +- src/game/Chat/Level2.cpp | 6 +- src/game/Chat/Level3.cpp | 12 +- src/game/GameEvents/GameEventMgr.cpp | 2 +- src/game/Maps/MapPersistentStateMgr.cpp | 4 +- src/game/Server/WorldSocket.cpp | 6 +- src/game/World/World.cpp | 20 +- src/realmd/AuthSocket.cpp | 10 +- src/realmd/Main.cpp | 4 +- src/shared/CMakeLists.txt | 7 + src/shared/Database/Database.h | 1 + src/shared/Database/DatabaseEnv.h | 21 ++ src/shared/Database/DatabaseMysql.cpp | 2 + src/shared/Database/DatabaseMysql.h | 2 + src/shared/Database/DatabaseSqlite.cpp | 384 ++++++++++++++++++++++ src/shared/Database/DatabaseSqlite.h | 105 ++++++ src/shared/Database/QueryResultMysql.cpp | 2 + src/shared/Database/QueryResultMysql.h | 2 + src/shared/Database/QueryResultSqlite.cpp | 106 ++++++ src/shared/Database/QueryResultSqlite.h | 55 ++++ src/shared/Database/SqlDelayThread.cpp | 4 + 23 files changed, 731 insertions(+), 35 deletions(-) create mode 100644 src/shared/Database/DatabaseSqlite.cpp create mode 100644 src/shared/Database/DatabaseSqlite.h create mode 100644 src/shared/Database/QueryResultSqlite.cpp create mode 100644 src/shared/Database/QueryResultSqlite.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b005386fa02..7d6e2b25d5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -251,6 +251,8 @@ endif() if(UNIX AND (BUILD_GAME_SERVER OR BUILD_LOGIN_SERVER OR BUILD_EXTRACTORS)) if(POSTGRESQL) find_package(PostgreSQL REQUIRED) + elseif(SQLITE) + find_package(SQLite3 REQUIRED) else() find_package(MySQL REQUIRED) endif() @@ -359,6 +361,8 @@ set(DEFINITIONS "") if(POSTGRESQL) set(DEFINITIONS ${DEFINITIONS} DO_POSTGRESQL) +elseif(SQLITE) + set(DEFINITIONS ${DEFINITIONS} DO_SQLITE) else() set(DEFINITIONS ${DEFINITIONS} DO_MYSQL) endif() diff --git a/src/game/Accounts/AccountMgr.cpp b/src/game/Accounts/AccountMgr.cpp index 559360b29db..008685db733 100644 --- a/src/game/Accounts/AccountMgr.cpp +++ b/src/game/Accounts/AccountMgr.cpp @@ -56,9 +56,8 @@ AccountOpResult AccountMgr::CreateAccount(std::string username, std::string pass const char* v_hex = srp.GetVerifier().AsHexStr(); bool update_sv = LoginDatabase.PExecute( - "INSERT INTO account(username,v,s,joindate) VALUES('%s','%s','%s',NOW())", + "INSERT INTO account(username,v,s,joindate) VALUES('%s','%s','%s'," _NOW_ ")", username.c_str(), v_hex, s_hex); - OPENSSL_free((void*)s_hex); OPENSSL_free((void*)v_hex); @@ -90,7 +89,7 @@ AccountOpResult AccountMgr::CreateAccount(std::string username, std::string pass const char* v_hex = srp.GetVerifier().AsHexStr(); bool update_sv = LoginDatabase.PExecute( - "INSERT INTO account(username,v,s,joindate,expansion) VALUES('%s','%s','%s',NOW(), %u)", + "INSERT INTO account(username,v,s,joindate,expansion) VALUES('%s','%s','%s'," _NOW_ ",%u)", username.c_str(), v_hex, s_hex, expansion); OPENSSL_free((void*)s_hex); diff --git a/src/game/BattleGround/BattleGround.cpp b/src/game/BattleGround/BattleGround.cpp index 3332b5e4c5a..e5e51eb37d6 100644 --- a/src/game/BattleGround/BattleGround.cpp +++ b/src/game/BattleGround/BattleGround.cpp @@ -775,7 +775,7 @@ void BattleGround::EndBattleGround(Team winner) { static SqlStatementID insPvPstatsBattleground; - SqlStatement stmt = CharacterDatabase.CreateStatement(insPvPstatsBattleground, "INSERT INTO pvpstats_battlegrounds (id, winner_team, bracket_id, type, date) VALUES (?, ?, ?, ?, NOW())"); + SqlStatement stmt = CharacterDatabase.CreateStatement(insPvPstatsBattleground, "INSERT INTO pvpstats_battlegrounds (id, winner_team, bracket_id, type, date) VALUES (?, ?, ?, ?, " _NOW_ ")"); uint8 battleground_bracket = GetMinLevel() / 10; uint8 battleground_type = (uint8)GetTypeId(); diff --git a/src/game/Chat/Level2.cpp b/src/game/Chat/Level2.cpp index 02382a5252e..f0fdd6befb4 100644 --- a/src/game/Chat/Level2.cpp +++ b/src/game/Chat/Level2.cpp @@ -849,7 +849,7 @@ bool ChatHandler::HandleGameObjectTargetCommand(char* args) uint32 id; if (ExtractUInt32(&cId, id)) { - queryResult = WorldDatabase.PQuery("SELECT guid, id, position_x, position_y, position_z, orientation, map, (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ FROM gameobject WHERE map = '%i' AND guid = '%u' ORDER BY order_ ASC LIMIT 1", + queryResult = WorldDatabase.PQuery("SELECT guid, id, position_x, position_y, position_z, orientation, map, (POW(position_x - %f, 2) + POW(position_y - %f, 2) + POW(position_z - %f, 2)) AS order_ FROM gameobject WHERE map = '%i' AND guid = '%u' ORDER BY order_ ASC LIMIT 1", pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), pl->GetMapId(), id); } else @@ -1227,8 +1227,8 @@ bool ChatHandler::HandleGameObjectNearCommand(char* args) Player* pl = m_session->GetPlayer(); auto queryResult = WorldDatabase.PQuery("SELECT guid, id, position_x, position_y, position_z, map, " - "(POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ " - "FROM gameobject WHERE map='%u' AND (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) <= '%f' ORDER BY order_", + "(POW(position_x - %f, 2) + POW(position_y - %f, 2) + POW(position_z - %f, 2)) AS order_ " + "FROM gameobject WHERE map='%u' AND (POW(position_x - %f, 2) + POW(position_y - %f, 2) + POW(position_z - %f, 2)) <= %f ORDER BY order_", pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), pl->GetMapId(), pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), distance * distance); diff --git a/src/game/Chat/Level3.cpp b/src/game/Chat/Level3.cpp index 8d84d31c795..8017d44e10c 100644 --- a/src/game/Chat/Level3.cpp +++ b/src/game/Chat/Level3.cpp @@ -5315,7 +5315,7 @@ bool ChatHandler::HandleBanInfoIPCommand(char* args) std::string IP = cIP; LoginDatabase.escape_string(IP); - auto queryResult = LoginDatabase.PQuery("SELECT ip, FROM_UNIXTIME(banned_at), FROM_UNIXTIME(expires_at), expires_at-UNIX_TIMESTAMP(), reason,banned_by,expires_at-banned_at" + auto queryResult = LoginDatabase.PQuery("SELECT ip, FROM_UNIXTIME(banned_at), FROM_UNIXTIME(expires_at), expires_at-" _UNIXTIME_ ", reason,banned_by,expires_at-banned_at" "FROM ip_banned WHERE ip = '%s'", IP.c_str()); if (!queryResult) { @@ -5333,7 +5333,7 @@ bool ChatHandler::HandleBanInfoIPCommand(char* args) bool ChatHandler::HandleBanListCharacterCommand(char* args) { - LoginDatabase.Execute("DELETE FROM ip_banned WHERE expires_at<=UNIX_TIMESTAMP() AND expires_at<>banned_at"); + LoginDatabase.Execute("DELETE FROM ip_banned WHERE expires_at<=" _UNIXTIME_ " AND expires_at<>banned_at"); char* cFilter = ExtractLiteralArg(&args); if (!cFilter) @@ -5353,7 +5353,7 @@ bool ChatHandler::HandleBanListCharacterCommand(char* args) bool ChatHandler::HandleBanListAccountCommand(char* args) { - LoginDatabase.Execute("DELETE FROM ip_banned WHERE expires_at<=UNIX_TIMESTAMP() AND expires_at<>banned_at"); + LoginDatabase.Execute("DELETE FROM ip_banned WHERE expires_at<=" _UNIXTIME_ " AND expires_at<>banned_at"); char* cFilter = ExtractLiteralArg(&args); std::string filter = cFilter ? cFilter : ""; @@ -5462,7 +5462,7 @@ bool ChatHandler::HandleBanListHelper(std::unique_ptr queryResult) bool ChatHandler::HandleBanListIPCommand(char* args) { - LoginDatabase.Execute("DELETE FROM ip_banned WHERE expires_at<=UNIX_TIMESTAMP() AND expires_at<>banned_at"); + LoginDatabase.Execute("DELETE FROM ip_banned WHERE expires_at<=" _UNIXTIME_ " AND expires_at<>banned_at"); char* cFilter = ExtractLiteralArg(&args); std::string filter = cFilter ? cFilter : ""; @@ -5473,13 +5473,13 @@ bool ChatHandler::HandleBanListIPCommand(char* args) if (filter.empty()) { queryResult = LoginDatabase.Query("SELECT ip,banned_at,expires_at,banned_by,reason FROM ip_banned" - " WHERE (banned_at=expires_at OR expires_at>UNIX_TIMESTAMP())" + " WHERE (banned_at=expires_at OR expires_at>" _UNIXTIME_ ")" " ORDER BY expires_at"); } else { queryResult = LoginDatabase.PQuery("SELECT ip,banned_at,expires_at,banned_by,reason FROM ip_banned" - " WHERE (banned_at=expires_at OR expires_at>UNIX_TIMESTAMP()) AND ip " _LIKE_ " " _CONCAT3_("'%%'", "'%s'", "'%%'") + " WHERE (banned_at=expires_at OR expires_at>" _UNIXTIME_ ") AND ip " _LIKE_ " " _CONCAT3_("'%%'", "'%s'", "'%%'") " ORDER BY expires_at", filter.c_str()); } diff --git a/src/game/GameEvents/GameEventMgr.cpp b/src/game/GameEvents/GameEventMgr.cpp index 29269916ecd..776e43b4378 100644 --- a/src/game/GameEvents/GameEventMgr.cpp +++ b/src/game/GameEvents/GameEventMgr.cpp @@ -670,7 +670,7 @@ uint32 GameEventMgr::Initialize() // return the next e } while (queryResult->NextRow()); - CharacterDatabase.Execute("TRUNCATE game_event_status"); + CharacterDatabase.Execute(_TRUNCATE_ " game_event_status"); } uint32 delay = Update(&activeAtShutdown); diff --git a/src/game/Maps/MapPersistentStateMgr.cpp b/src/game/Maps/MapPersistentStateMgr.cpp index 68d25ee6fcc..d566378cf3c 100644 --- a/src/game/Maps/MapPersistentStateMgr.cpp +++ b/src/game/Maps/MapPersistentStateMgr.cpp @@ -1001,7 +1001,7 @@ void MapPersistentStateManager::InitWorldMaps() void MapPersistentStateManager::LoadCreatureRespawnTimes() { // remove outdated data - CharacterDatabase.DirectExecute("DELETE FROM creature_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())"); + CharacterDatabase.DirectExecute("DELETE FROM creature_respawn WHERE respawntime <= " _UNIXNOW_); uint32 count = 0; @@ -1070,7 +1070,7 @@ void MapPersistentStateManager::LoadCreatureRespawnTimes() void MapPersistentStateManager::LoadGameobjectRespawnTimes() { // remove outdated data - CharacterDatabase.DirectExecute("DELETE FROM gameobject_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())"); + CharacterDatabase.DirectExecute("DELETE FROM gameobject_respawn WHERE respawntime <= " _UNIXNOW_); uint32 count = 0; diff --git a/src/game/Server/WorldSocket.cpp b/src/game/Server/WorldSocket.cpp index 56f7d315dd0..935a7138c1a 100644 --- a/src/game/Server/WorldSocket.cpp +++ b/src/game/Server/WorldSocket.cpp @@ -423,9 +423,9 @@ bool WorldSocket::HandleAuthSession(WorldPacket& recvPacket) // Re-check account ban (same check as in realmd) auto banresult = - LoginDatabase.PQuery("SELECT 1 FROM account_banned WHERE account_id = %u AND active = 1 AND (expires_at > UNIX_TIMESTAMP() OR expires_at = banned_at)" + LoginDatabase.PQuery("SELECT 1 FROM account_banned WHERE account_id = %u AND active = 1 AND (expires_at > " _UNIXTIME_ " OR expires_at = banned_at)" "UNION " - "SELECT 1 FROM ip_banned WHERE (expires_at = banned_at OR expires_at > UNIX_TIMESTAMP()) AND ip = '%s'", + "SELECT 1 FROM ip_banned WHERE (expires_at = banned_at OR expires_at > " _UNIXTIME_ ") AND ip = '%s'", id, GetRemoteAddress().c_str()); if (banresult) // if account banned @@ -497,7 +497,7 @@ bool WorldSocket::HandleAuthSession(WorldPacket& recvPacket) // No SQL injection, username escaped. static SqlStatementID updAccount; - SqlStatement stmt = LoginDatabase.CreateStatement(updAccount, "INSERT INTO account_logons(accountId,ip,loginTime,loginSource) VALUES(?,?,NOW(),?)"); + SqlStatement stmt = LoginDatabase.CreateStatement(updAccount, "INSERT INTO account_logons(accountId,ip,loginTime,loginSource) VALUES(?,?," _NOW_ ",?)"); stmt.PExecute(id, address.c_str(), std::to_string(LOGIN_TYPE_MANGOSD).c_str()); m_crypt.Init(&K); diff --git a/src/game/World/World.cpp b/src/game/World/World.cpp index d3c725e98dd..31f8c239145 100644 --- a/src/game/World/World.cpp +++ b/src/game/World/World.cpp @@ -888,7 +888,7 @@ void World::SetInitialWorldSettings() LoginDatabase.PExecute("UPDATE realmlist SET icon = %u, timezone = %u WHERE id = '%u'", server_type, realm_zone, realmID); ///- Remove the bones (they should not exist in DB though) and old corpses after a restart - CharacterDatabase.PExecute("DELETE FROM corpse WHERE corpse_type = '0' OR time < (UNIX_TIMESTAMP()-'%u')", 3 * DAY); + CharacterDatabase.PExecute("DELETE FROM corpse WHERE corpse_type = '0' OR time < (" _UNIXTIME_ "-'%u')", 3 * DAY); /// load spell_dbc first! dbc's need them sLog.outString("Loading spell_template..."); @@ -1379,7 +1379,8 @@ void World::SetInitialWorldSettings() CheckLootTemplates_Reference(ids_set); sLog.outString("Deleting expired bans..."); - LoginDatabase.Execute("DELETE FROM ip_banned WHERE expires_at<=UNIX_TIMESTAMP() AND expires_at<>banned_at"); + + LoginDatabase.Execute("DELETE FROM ip_banned WHERE expires_at<=" _UNIXTIME_ " AND expires_at<>banned_at"); sLog.outString(); sLog.outString("Calculate next daily quest reset time..."); @@ -1884,17 +1885,17 @@ void World::WarnAccount(uint32 accountId, std::string from, std::string reason, reason = std::string(type) + ": " + reason; LoginDatabase.escape_string(reason); - LoginDatabase.PExecute("INSERT INTO account_banned (account_id, banned_at, expires_at, banned_by, reason, active) VALUES ('%u', UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+1, '%s', '%s', '0')", + LoginDatabase.PExecute("INSERT INTO account_banned (account_id, banned_at, expires_at, banned_by, reason, active) VALUES ('%u', " _UNIXTIME_ ", " _UNIXTIME_ "+1, '%s', '%s', '0')", accountId, from.c_str(), reason.c_str()); } BanReturn World::BanAccount(WorldSession *session, uint32 duration_secs, const std::string& reason, const std::string& author) { if (duration_secs) - LoginDatabase.PExecute("INSERT INTO account_banned(account_id, banned_at, expires_at, banned_by, reason, active) VALUES ('%u', UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+%u, '%s', '%s', '1')", + LoginDatabase.PExecute("INSERT INTO account_banned(account_id, banned_at, expires_at, banned_by, reason, active) VALUES ('%u', " _UNIXTIME_ ", " _UNIXTIME_ "+%u, '%s', '%s', '1')", session->GetAccountId(), duration_secs, author.c_str(), reason.c_str()); else - LoginDatabase.PExecute("INSERT INTO account_banned(account_id, banned_at, expires_at, banned_by, reason, active) VALUES ('%u', UNIX_TIMESTAMP(), 0, '%s', '%s', '1')", + LoginDatabase.PExecute("INSERT INTO account_banned(account_id, banned_at, expires_at, banned_by, reason, active) VALUES ('%u', " _UNIXTIME_ ", 0, '%s', '%s', '1')", session->GetAccountId(), author.c_str(), reason.c_str()); session->KickPlayer(); @@ -1917,7 +1918,7 @@ BanReturn World::BanAccount(BanMode mode, std::string nameOrIP, uint32 duration_ case BAN_IP: // No SQL injection as strings are escaped resultAccounts = LoginDatabase.PQuery("SELECT accountId FROM account_logons WHERE ip = '%s' ORDER BY loginTime DESC LIMIT 1", nameOrIP.c_str()); - LoginDatabase.PExecute("INSERT INTO ip_banned VALUES ('%s',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+%u,'%s','%s')", nameOrIP.c_str(), duration_secs, safe_author.c_str(), reason.c_str()); + LoginDatabase.PExecute("INSERT INTO ip_banned VALUES ('%s'," _UNIXTIME_ "," _UNIXTIME_ "+%u,'%s','%s')", nameOrIP.c_str(), duration_secs, safe_author.c_str(), reason.c_str()); break; case BAN_ACCOUNT: // No SQL injection as string is escaped @@ -1948,7 +1949,7 @@ BanReturn World::BanAccount(BanMode mode, std::string nameOrIP, uint32 duration_ if (mode != BAN_IP) { // No SQL injection as strings are escaped - LoginDatabase.PExecute("INSERT INTO account_banned(account_id, banned_at, expires_at, banned_by, reason, active) VALUES ('%u', UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+%u, '%s', '%s', '1')", + LoginDatabase.PExecute("INSERT INTO account_banned(account_id, banned_at, expires_at, banned_by, reason, active) VALUES ('%u', " _UNIXTIME_ ", " _UNIXTIME_ "+%u, '%s', '%s', '1')", account, duration_secs, safe_author.c_str(), reason.c_str()); } @@ -1981,7 +1982,7 @@ bool World::RemoveBanAccount(BanMode mode, const std::string& source, const std: return false; // NO SQL injection as account is uint32 - LoginDatabase.PExecute("UPDATE account_banned SET active = '0', unbanned_at = UNIX_TIMESTAMP(), unbanned_by = '%s' WHERE account_id = '%u'", source.data(), account); + LoginDatabase.PExecute("UPDATE account_banned SET active = '0', unbanned_at = " _UNIXTIME_ ", unbanned_by = '%s' WHERE account_id = '%u'", source.data(), account); WarnAccount(account, source, message, "UNBAN"); } return true; @@ -2387,7 +2388,8 @@ void World::ResetWeeklyQuests() void World::ResetMonthlyQuests() { DETAIL_LOG("Monthly quests reset for all characters."); - CharacterDatabase.Execute("TRUNCATE character_queststatus_monthly"); + + CharacterDatabase.Execute(_TRUNCATE_ " character_queststatus_monthly"); for (SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) if (itr->second->GetPlayer()) diff --git a/src/realmd/AuthSocket.cpp b/src/realmd/AuthSocket.cpp index 1821e054de4..2789b91a4c1 100644 --- a/src/realmd/AuthSocket.cpp +++ b/src/realmd/AuthSocket.cpp @@ -382,7 +382,7 @@ bool AuthSocket::_HandleLogonChallenge() ///- Verify that this IP is not in the ip_banned table // No SQL injection possible (paste the IP address as passed by the socket) std::unique_ptr ip_banned_result(LoginDatabase.PQuery("SELECT expires_at FROM ip_banned " - "WHERE (expires_at = banned_at OR expires_at > UNIX_TIMESTAMP()) AND ip = '%s'", m_address.c_str())); + "WHERE (expires_at = banned_at OR expires_at > " _UNIXTIME_ ") AND ip = '%s'", m_address.c_str())); if (ip_banned_result) { @@ -431,7 +431,7 @@ bool AuthSocket::_HandleLogonChallenge() { ///- If the account is banned, reject the logon attempt auto banresult = LoginDatabase.PQuery("SELECT banned_at,expires_at FROM account_banned WHERE " - "account_id = %u AND active = 1 AND (expires_at > UNIX_TIMESTAMP() OR expires_at = banned_at)", fields[0].GetUInt32()); + "account_id = %u AND active = 1 AND (expires_at > " _UNIXTIME_ " OR expires_at = banned_at)", fields[0].GetUInt32()); if (banresult) { if ((*banresult)[0].GetUInt64() == (*banresult)[1].GetUInt64()) @@ -595,7 +595,7 @@ bool AuthSocket::_HandleLogonProof() LoginDatabase.PExecute("UPDATE account SET sessionkey = '%s', locale = '%s', failed_logins = 0, os = '%s', platform = '%s' WHERE username = '%s'", K_hex, _safelocale.c_str(), m_os.c_str(), m_platform.c_str(), _safelogin.c_str()); std::unique_ptr loginfail(LoginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'", _safelogin.c_str())); if (loginfail) - LoginDatabase.PExecute("INSERT INTO account_logons(accountId,ip,loginTime,loginSource) VALUES('%u','%s',NOW(),'%u')", loginfail->Fetch()[0].GetUInt32(), m_address.c_str(), LOGIN_TYPE_REALMD); + LoginDatabase.PExecute("INSERT INTO account_logons(accountId,ip,loginTime,loginSource) VALUES('%u','%s'," _NOW_ ",'%u')", loginfail->Fetch()[0].GetUInt32(), m_address.c_str(), LOGIN_TYPE_REALMD); OPENSSL_free((void*)K_hex); ///- Finish SRP6 and send the final result to the client @@ -643,7 +643,7 @@ bool AuthSocket::_HandleLogonProof() { uint32 acc_id = fields[0].GetUInt32(); LoginDatabase.PExecute("INSERT INTO account_banned(account_id, banned_at, expires_at, banned_by, reason, active)" - "VALUES ('%u',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+'%u','MaNGOS realmd','Failed login autoban',1)", + "VALUES ('%u'," _UNIXTIME_ "," _UNIXTIME_ "+'%u','MaNGOS realmd','Failed login autoban',1)", acc_id, WrongPassBanTime); BASIC_LOG("[AuthChallenge] account %s got banned for '%u' seconds because it failed to authenticate '%u' times", _login.c_str(), WrongPassBanTime, failed_logins); @@ -652,7 +652,7 @@ bool AuthSocket::_HandleLogonProof() { std::string current_ip = m_address; LoginDatabase.escape_string(current_ip); - LoginDatabase.PExecute("INSERT INTO ip_banned VALUES ('%s',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+'%u','MaNGOS realmd','Failed login autoban')", + LoginDatabase.PExecute("INSERT INTO ip_banned VALUES ('%s'," _UNIXTIME_ "," _UNIXTIME_ "+'%u','MaNGOS realmd','Failed login autoban')", current_ip.c_str(), WrongPassBanTime); BASIC_LOG("[AuthChallenge] IP %s got banned for '%u' seconds because account %s failed to authenticate '%u' times", current_ip.c_str(), WrongPassBanTime, _login.c_str(), failed_logins); diff --git a/src/realmd/Main.cpp b/src/realmd/Main.cpp index acc40bcfc40..f5eac9f89c4 100644 --- a/src/realmd/Main.cpp +++ b/src/realmd/Main.cpp @@ -228,8 +228,8 @@ int main(int argc, char* argv[]) // cleanup query // set expired bans to inactive LoginDatabase.BeginTransaction(); - LoginDatabase.Execute("UPDATE account_banned SET active = 0 WHERE expires_at<=UNIX_TIMESTAMP() AND expires_at<>banned_at"); - LoginDatabase.Execute("DELETE FROM ip_banned WHERE expires_at<=UNIX_TIMESTAMP() AND expires_at<>banned_at"); + LoginDatabase.Execute("UPDATE account_banned SET active = 0 WHERE expires_at<=" _UNIXTIME_ " AND expires_at<>banned_at"); + LoginDatabase.Execute("DELETE FROM ip_banned WHERE expires_at<=" _UNIXTIME_ " AND expires_at<>banned_at"); LoginDatabase.CommitTransaction(); // FIXME - more intelligent selection of thread count is needed here. config option? diff --git a/src/shared/CMakeLists.txt b/src/shared/CMakeLists.txt index 89dd9dd54b1..fa84c8ef9d1 100644 --- a/src/shared/CMakeLists.txt +++ b/src/shared/CMakeLists.txt @@ -46,6 +46,8 @@ set(SRC_GRP_DATABASE Database/DatabaseMysql.h Database/DatabasePostgre.cpp Database/DatabasePostgre.h + Database/DatabaseSqlite.cpp + Database/DatabaseSqlite.h Database/Field.cpp Database/Field.h Database/PGSQLDelayThread.h @@ -54,6 +56,8 @@ set(SRC_GRP_DATABASE Database/QueryResultMysql.h Database/QueryResultPostgre.cpp Database/QueryResultPostgre.h + Database/QueryResultSqlite.cpp + Database/QueryResultSqlite.h Database/SqlDelayThread.cpp Database/SqlDelayThread.h Database/SqlOperations.cpp @@ -244,6 +248,9 @@ endif() if(POSTGRESQL AND POSTGRESQL_FOUND) target_include_directories(${LIBRARY_NAME} PUBLIC ${PostgreSQL_INCLUDE_DIRS}) target_link_libraries(${LIBRARY_NAME} PUBLIC ${PostgreSQL_LIBRARIES}) +elseif(SQLITE AND SQLite3_FOUND) + target_include_directories(${LIBRARY_NAME} PUBLIC ${SQLite3_INCLUDE_DIRS}) + target_link_libraries(${LIBRARY_NAME} PUBLIC ${SQLite3_LIBRARIES}) else() target_include_directories(${LIBRARY_NAME} PUBLIC ${MYSQL_INCLUDE_DIR}) target_link_libraries(${LIBRARY_NAME} PUBLIC ${MYSQL_LIBRARY}) diff --git a/src/shared/Database/Database.h b/src/shared/Database/Database.h index 80941aaeac6..e11773b8fa3 100644 --- a/src/shared/Database/Database.h +++ b/src/shared/Database/Database.h @@ -24,6 +24,7 @@ #include "Database/SqlDelayThread.h" #include "Policies/ThreadingModel.h" #include "SqlPreparedStatement.h" +#include "QueryResult.h" #include #include diff --git a/src/shared/Database/DatabaseEnv.h b/src/shared/Database/DatabaseEnv.h index ea7b4658ef1..a21b4d03564 100644 --- a/src/shared/Database/DatabaseEnv.h +++ b/src/shared/Database/DatabaseEnv.h @@ -35,6 +35,23 @@ typedef DatabasePostgre DatabaseType; #define _TABLE_SIM_ "\"" #define _CONCAT3_(A,B,C) "( " A " || " B " || " C " )" #define _OFFSET_ "LIMIT 1 OFFSET %d" +#define _TRUNCATE_ "TRUNCATE TABLE" +#define _NOW_ "NOW()" +#define _UNIXTIME_ "UNIX_TIMESTAMP()" +#define _UNIXNOW_ "UNIX_TIMESTAMP(NOW())" +#elif DO_SQLITE +#include "Database/QueryResultSqlite.h" +#include "Database/Database.h" +#include "Database/DatabaseSqlite.h" +typedef DatabaseSqlite DatabaseType; +#define _LIKE_ "LIKE" +#define _TABLE_SIM_ '`' +#define _CONCAT3_(A,B,C) "( " A " || " B " || " C " )" +#define _OFFSET_ "LIMIT %d,1" +#define _TRUNCATE_ "DELETE FROM" +#define _NOW_ "datetime()" +#define _UNIXTIME_ "unixepoch()" +#define _UNIXNOW_ "unixepoch('now')" #else #include "Database/QueryResultMysql.h" #include "Database/Database.h" @@ -44,6 +61,10 @@ typedef DatabaseMysql DatabaseType; #define _TABLE_SIM_ '`' #define _CONCAT3_(A,B,C) "CONCAT( " A " , " B " , " C " )" #define _OFFSET_ "LIMIT %d,1" +#define _TRUNCATE_ "TRUNCATE TABLE" +#define _NOW_ "NOW()" +#define _UNIXTIME_ "UNIX_TIMESTAMP()" +#define _UNIXNOW_ "UNIX_TIMESTAMP(NOW())" #endif extern DatabaseType WorldDatabase; diff --git a/src/shared/Database/DatabaseMysql.cpp b/src/shared/Database/DatabaseMysql.cpp index c10d03ce672..4bd4b2069c9 100644 --- a/src/shared/Database/DatabaseMysql.cpp +++ b/src/shared/Database/DatabaseMysql.cpp @@ -17,6 +17,7 @@ */ #ifndef DO_POSTGRESQL +#ifndef DO_SQLITE #include "Util/Util.h" #include "Policies/Singleton.h" @@ -487,3 +488,4 @@ enum_field_types MySqlPreparedStatement::ToMySQLType(const SqlStmtFieldData& dat return dataType; } #endif +#endif diff --git a/src/shared/Database/DatabaseMysql.h b/src/shared/Database/DatabaseMysql.h index c3db0c1d983..bdf0dcb05be 100644 --- a/src/shared/Database/DatabaseMysql.h +++ b/src/shared/Database/DatabaseMysql.h @@ -17,6 +17,7 @@ */ #ifndef DO_POSTGRESQL +#ifndef DO_SQLITE #ifndef _DATABASEMYSQL_H #define _DATABASEMYSQL_H @@ -111,3 +112,4 @@ class DatabaseMysql : public Database #endif #endif +#endif diff --git a/src/shared/Database/DatabaseSqlite.cpp b/src/shared/Database/DatabaseSqlite.cpp new file mode 100644 index 00000000000..a0e05a2edee --- /dev/null +++ b/src/shared/Database/DatabaseSqlite.cpp @@ -0,0 +1,384 @@ +/* + * This file is part of the CMaNGOS Project. See AUTHORS file for Copyright information + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "QueryResultSqlite.h" +#include +#include +#include +#ifdef DO_SQLITE + +#include "Util/Util.h" +#include "Policies/Singleton.h" +#include "Platform/Define.h" +#include "Multithreading/Threading.h" +#include "DatabaseEnv.h" +#include "Util/Timer.h" +#include "DatabaseSqlite.h" + +size_t DatabaseSqlite::db_count = 0; + +DatabaseSqlite::DatabaseSqlite() +{ + // before first connection + if (db_count++ == 0); +} + +DatabaseSqlite::~DatabaseSqlite() +{ + StopServer(); + if (--db_count == 0); +} + +SqlConnection* DatabaseSqlite::CreateConnection() +{ + return new SQLiteConnection(*this); +} + +SQLiteConnection::~SQLiteConnection() +{ + FreePreparedStatements(); + sqlite3_exec(mSqlite, "PRAGMA optimize;", 0, 0, 0); + sqlite3_close(mSqlite); +} + +bool SQLiteConnection::Initialize(const char* infoString) +{ + if (sqlite3_open(infoString, &mSqlite) != SQLITE_OK) + { + sLog.outError("Could not open SQLite database"); + return false; + } + sqlite3_exec(mSqlite, "PRAGMA journal_mode=WAL;", 0, 0, 0); + sqlite3_exec(mSqlite, "PRAGMA synchronous=1;", 0, 0, 0); + sqlite3_exec(mSqlite, "PRAGMA secure_delete=off;", 0, 0, 0); + sqlite3_exec(mSqlite, "PRAGMA encoding='UTF-8';", 0, 0, 0); + sqlite3_busy_timeout(mSqlite, 2); + + Tokens tokens = StrSplit(infoString, ";"); + + DETAIL_LOG("Connected to SQLite database at %s", infoString); + + return true; +} + +bool SQLiteConnection::_Query(const char* sql, sqlite3_stmt** pStmt) +{ + if (!mSqlite) + return false; + + uint32 _s = WorldTimer::getMSTime(); + int result; + result = sqlite3_prepare_v2(mSqlite, sql, -1, pStmt, NULL); + if (result != SQLITE_OK) { + sLog.outErrorDb("SQL: %s", sql); + sLog.outErrorDb("query ERROR: %s", sqlite3_errmsg(mSqlite)); + return false; + } + + return true; +} + +std::unique_ptr SQLiteConnection::Query(const char* sql) +{ + sqlite3_stmt** pStmt = new(sqlite3_stmt*); + if (!_Query(sql, pStmt)) + return nullptr; + + auto queryResult = std::make_unique(pStmt); + + if (queryResult->NextRow()) + return queryResult; + return nullptr; +} + +QueryNamedResult* SQLiteConnection::QueryNamed(const char* sql) +{ + uint64 rowCount = 0; + uint32 fieldCount = 0; + + sqlite3_stmt** pStmt = new(sqlite3_stmt*); + + if (!_Query(sql, pStmt)) + return nullptr; + + fieldCount = sqlite3_column_count(*pStmt); + + QueryFieldNames names(fieldCount); + for (uint32 i = 0; i < fieldCount; ++i) + names[i] = sqlite3_column_name(*pStmt, i); + + QueryResultSqlite* queryResult = new QueryResultSqlite(pStmt); + + queryResult->NextRow(); + return new QueryNamedResult(queryResult, names); +} + +bool SQLiteConnection::Execute(const char* sql) +{ + if (!mSqlite) + return false; + + { + uint32 _s = WorldTimer::getMSTime(); + sqlite3_stmt** pStmt = new(sqlite3_stmt*); + + int result; + result = sqlite3_prepare_v2(mSqlite, sql, -1, pStmt, NULL); + if (result != SQLITE_OK) + { + sLog.outErrorDb("SQL: %s", sql); + sLog.outErrorDb("SQL ERROR: %s", sqlite3_errmsg(mSqlite)); + return false; + } + result = sqlite3_step(*pStmt); + if (result != SQLITE_DONE && result != SQLITE_OK) + { + sLog.outErrorDb("SQL: %s", sql); + sLog.outErrorDb("SQL ERROR: %s", sqlite3_errmsg(mSqlite)); + return false; + } + sqlite3_finalize(*pStmt); + DEBUG_FILTER_LOG(LOG_FILTER_SQL_TEXT, "[%u ms] SQL: %s", WorldTimer::getMSTimeDiff(_s, WorldTimer::getMSTime()), sql); + // end guarded block + } + + return true; +} + +bool SQLiteConnection::_TransactionCmd(const char* sql) +{ + sqlite3_stmt** pStmt = new(sqlite3_stmt*); + + int result; + result = sqlite3_prepare_v2(mSqlite, sql, -1, pStmt, NULL); + if (result != SQLITE_OK) + { + sLog.outErrorDb("SQL: %s", sql); + sLog.outErrorDb("SQL ERROR: %s", sqlite3_errmsg(mSqlite)); + return false; + } + result = sqlite3_step(*pStmt); + if (result != SQLITE_DONE && result != SQLITE_OK) + { + sLog.outErrorDb("SQL: %s", sql); + sLog.outErrorDb("SQL ERROR: %s", sqlite3_errmsg(mSqlite)); + return false; + } + DEBUG_FILTER_LOG(LOG_FILTER_SQL_TEXT, "SQL: %s", sql); + return true; +} + +bool SQLiteConnection::BeginTransaction() +{ + return _TransactionCmd("BEGIN"); +} + +bool SQLiteConnection::CommitTransaction() +{ + return _TransactionCmd("COMMIT"); +} + +bool SQLiteConnection::RollbackTransaction() +{ + return _TransactionCmd("ROLLBACK"); +} + +unsigned long SQLiteConnection::escape_string(char* to, const char* from, unsigned long length) +{ + if (!mSqlite || !to || !from || !length) + return 0; + std::string newFrom(from); + std::string newTo; + + for (const char& c : newFrom) + { + switch (c) + { + [[unlikely]] case '\'': newTo += "''"; break; + [[unlikely]] case '\\': newTo += "\\"; break; + [[likely]] default: newTo += c; break; + } + } + strcpy(to, newTo.c_str()); + + return newTo.length(); +} + +////////////////////////////////////////////////////////////////////////// +SqlPreparedStatement* SQLiteConnection::CreateStatement(const std::string& fmt) +{ + return new SqlitePreparedStatement(fmt, *this, mSqlite); +} + +////////////////////////////////////////////////////////////////////////// +SqlitePreparedStatement::SqlitePreparedStatement(const std::string& fmt, SqlConnection& conn, sqlite3* mysql) : SqlPreparedStatement(fmt, conn), + m_pSqliteConn(mysql), m_stmt(nullptr), m_pResult(nullptr) +{ +} + +SqlitePreparedStatement::~SqlitePreparedStatement() +{ + RemoveBinds(); +} + +bool SqlitePreparedStatement::prepare() +{ + if (isPrepared()) + return true; + + // remove old binds + RemoveBinds(); + + if (!m_stmt) + m_stmt = new(sqlite3_stmt*); + + // Create statement object + int result = sqlite3_prepare_v2(m_pSqliteConn, m_szFmt.c_str(), m_szFmt.length(), m_stmt, nullptr); + if (result != SQLITE_OK) { + sLog.outError("SQL: sqlite3_prepare_v2() failed: %s", sqlite3_errmsg(m_pSqliteConn)); + return false; + } + // Get the parameter count from the statement + m_nParams = sqlite3_bind_parameter_count(*m_stmt); + + // Check if we have a statement which returns result sets + if (sqlite3_stmt_readonly(*m_stmt) == 0) { + // Our statement is a query + m_bIsQuery = true; + m_nColumns = sqlite3_column_count(*m_stmt); // Get the number of columns in the result set + } + + m_bPrepared = true; + return true; +} + +void SqlitePreparedStatement::bind(const SqlStmtParameters& holder) +{ + if (!isPrepared()) + { + MANGOS_ASSERT(!isPrepared()); + return; + } + + // verify if we bound all needed input parameters + if (m_nParams != holder.boundParams()) + { + MANGOS_ASSERT(m_nParams != holder.boundParams()); + return; + } + + unsigned int nIndex = 0; // SQLite uses 1-based index for parameter binding + SqlStmtParameters::ParameterContainer const& _args = holder.params(); + + for (const auto& param : _args) + { + // Bind parameter + addParam(nIndex, param); + nIndex++; + } +} + +void SqlitePreparedStatement::addParam(unsigned int nIndex, const SqlStmtFieldData& data) +{ + MANGOS_ASSERT(nIndex < m_nParams); + + // SQLite uses different types and structures for binding parameters + int result = SQLITE_OK; + + switch (data.type()) { + case FIELD_BOOL: + case FIELD_UI8: + result = sqlite3_bind_int(*m_stmt, nIndex + 1, *static_cast(data.buff())); + break; + case FIELD_UI16: + result = sqlite3_bind_int(*m_stmt, nIndex + 1, *static_cast(data.buff())); + break; + case FIELD_UI32: + result = sqlite3_bind_int(*m_stmt, nIndex + 1, *static_cast(data.buff())); + break; + case FIELD_UI64: + result = sqlite3_bind_int64(*m_stmt, nIndex + 1, *static_cast(data.buff())); + break; + case FIELD_I8: + result = sqlite3_bind_int(*m_stmt, nIndex + 1, *static_cast(data.buff())); + break; + case FIELD_I16: + result = sqlite3_bind_int(*m_stmt, nIndex + 1, *static_cast(data.buff())); + break; + case FIELD_I32: + result = sqlite3_bind_int(*m_stmt, nIndex + 1, *static_cast(data.buff())); + break; + case FIELD_I64: + result = sqlite3_bind_int64(*m_stmt, nIndex + 1, *static_cast(data.buff())); + break; + case FIELD_FLOAT: + result = sqlite3_bind_double(*m_stmt, nIndex + 1, *static_cast(data.buff())); + break; + case FIELD_DOUBLE: + result = sqlite3_bind_double(*m_stmt, nIndex + 1, *static_cast(data.buff())); + break; + case FIELD_STRING: + result = sqlite3_bind_text(*m_stmt, nIndex + 1, static_cast(data.buff()), -1, SQLITE_STATIC); + break; + case FIELD_NONE: + result = sqlite3_bind_null(*m_stmt, nIndex + 1); + break; + // Handle other data types as needed + + default: + MANGOS_ASSERT(false && "Unsupported parameter type"); + } + + if (result != SQLITE_OK) { + // Handle the error, e.g., print an error message + fprintf(stderr, "Error binding parameter at index %d: %s\n", nIndex, sqlite3_errmsg(m_pSqliteConn)); + } +} + +void SqlitePreparedStatement::RemoveBinds() +{ + if (!m_stmt) + return; + + // Finalize the prepared statement + sqlite3_finalize(*m_stmt); + *m_stmt = nullptr; + + m_bPrepared = false; +} + +bool SqlitePreparedStatement::execute() +{ + if (!isPrepared()) + return false; + + int result = sqlite3_step(*m_stmt); + + if (result != SQLITE_DONE) + { + sLog.outErrorDb("SQL: cannot execute '%s'", m_szFmt.c_str()); + sLog.outErrorDb("SQL ERROR: %s", sqlite3_errmsg(m_pSqliteConn)); + return false; + } + + // Reset the prepared statement to be executed again if needed + sqlite3_reset(*m_stmt); + + return true; +} +#endif diff --git a/src/shared/Database/DatabaseSqlite.h b/src/shared/Database/DatabaseSqlite.h new file mode 100644 index 00000000000..e2c1eb548ad --- /dev/null +++ b/src/shared/Database/DatabaseSqlite.h @@ -0,0 +1,105 @@ +/* + * This file is part of the CMaNGOS Project. See AUTHORS file for Copyright information + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DO_POSTGRESQL + +#ifndef _DatabaseSqlite_H +#define _DatabaseSqlite_H + +//#include "Common.h" +#include "Database.h" +#include "Policies/Singleton.h" +#include "QueryResultSqlite.h" + +#include + +// MySQL prepared statement class +class SqlitePreparedStatement : public SqlPreparedStatement +{ + public: + SqlitePreparedStatement(const std::string& fmt, SqlConnection& conn, sqlite3* mysql); + ~SqlitePreparedStatement(); + + // prepare statement + virtual bool prepare() override; + + // bind input parameters + virtual void bind(const SqlStmtParameters& holder) override; + + // execute DML statement + virtual bool execute() override; + + protected: + // bind parameters + void addParam(unsigned int nIndex, const SqlStmtFieldData& data); + + private: + void RemoveBinds(); + + sqlite3* m_pSqliteConn; + sqlite3_stmt** m_stmt; + sqlite3_value* m_pResult; +}; + +class SQLiteConnection : public SqlConnection +{ + public: + SQLiteConnection(Database& db) : SqlConnection(db), mSqlite(nullptr) {} + ~SQLiteConnection(); + + //! Initializes Mysql and connects to a server. + /*! infoString should be formated like hostname;username;password;database. */ + bool Initialize(const char* infoString) override; + + std::unique_ptr Query(const char* sql) override; + QueryNamedResult* QueryNamed(const char* sql) override; + bool Execute(const char* sql) override; + + unsigned long escape_string(char* to, const char* from, unsigned long length); + + bool BeginTransaction() override; + bool CommitTransaction() override; + bool RollbackTransaction() override; + + protected: + SqlPreparedStatement* CreateStatement(const std::string& fmt) override; + + private: + bool _TransactionCmd(const char* sql); + bool _Query(const char* sql, sqlite3_stmt** pStmt); + + sqlite3* mSqlite; +}; + +class DatabaseSqlite : public Database +{ + friend class MaNGOS::OperatorNew; + + public: + DatabaseSqlite(); + ~DatabaseSqlite(); + + protected: + virtual SqlConnection* CreateConnection() override; + + private: + static size_t db_count; +}; + +#endif +#endif diff --git a/src/shared/Database/QueryResultMysql.cpp b/src/shared/Database/QueryResultMysql.cpp index f9fec84815a..1c3f1ee9e7b 100644 --- a/src/shared/Database/QueryResultMysql.cpp +++ b/src/shared/Database/QueryResultMysql.cpp @@ -17,6 +17,7 @@ */ #ifndef DO_POSTGRESQL +#ifndef DO_SQLITE #include "DatabaseEnv.h" #include "Util/Errors.h" @@ -97,3 +98,4 @@ enum Field::DataTypes QueryResultMysql::ConvertNativeType(enum_field_types mysql } } #endif +#endif diff --git a/src/shared/Database/QueryResultMysql.h b/src/shared/Database/QueryResultMysql.h index e60745998fb..ddf1fe22af2 100644 --- a/src/shared/Database/QueryResultMysql.h +++ b/src/shared/Database/QueryResultMysql.h @@ -17,6 +17,7 @@ */ #ifndef DO_POSTGRESQL +#ifndef DO_SQLITE #if !defined(QUERYRESULTMYSQL_H) #define QUERYRESULTMYSQL_H @@ -46,3 +47,4 @@ class QueryResultMysql : public QueryResult }; #endif #endif +#endif diff --git a/src/shared/Database/QueryResultSqlite.cpp b/src/shared/Database/QueryResultSqlite.cpp new file mode 100644 index 00000000000..89f682aba06 --- /dev/null +++ b/src/shared/Database/QueryResultSqlite.cpp @@ -0,0 +1,106 @@ +/* + * This file is part of the CMaNGOS Project. See AUTHORS file for Copyright information + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef DO_SQLITE + +#include "DatabaseEnv.h" +#include "Util/Errors.h" +#include "sqlite3.h" +#include "QueryResultSqlite.h" + +QueryResultSqlite::QueryResultSqlite(sqlite3_stmt** stmt) : + QueryResult(0, 0), mStmt(stmt) +{ + if (mStmt && *mStmt) + { + while (sqlite3_step(*mStmt) == SQLITE_ROW) + { + // Process each row's data here + mRowCount++; + } + sqlite3_reset(*mStmt); + mFieldCount = sqlite3_column_count(*mStmt); + mCurrentRow = new Field[mFieldCount]; + MANGOS_ASSERT(mCurrentRow); + + for (int i = 0; i < mFieldCount; ++i) + { + const unsigned char* value = sqlite3_column_text(*mStmt, i); + mCurrentRow[i].SetValue(value ? reinterpret_cast(value) : ""); + mCurrentRow[i].SetType(ConvertNativeType(sqlite3_column_type(*mStmt, i))); + } + } +} + +QueryResultSqlite::~QueryResultSqlite() +{ + EndQuery(); +} + +bool QueryResultSqlite::NextRow() +{ + if (!mStmt || !(*mStmt)) + return false; + + int rc = sqlite3_step(*mStmt); + if (rc != SQLITE_ROW) + { + EndQuery(); + return false; + } + + for (int i = 0; i < mFieldCount; ++i) + { + const unsigned char* value = sqlite3_column_text(*mStmt, i); + mCurrentRow[i].SetValue(value ? reinterpret_cast(value) : ""); + mCurrentRow[i].SetType(ConvertNativeType(sqlite3_column_type(*mStmt, i))); + } + + return true; +} + +void QueryResultSqlite::EndQuery() +{ + if (mStmt && *mStmt) + { + sqlite3_finalize(*mStmt); + *mStmt = nullptr; + //mStmt = nullptr; + } + + delete[] mCurrentRow; + mCurrentRow = nullptr; +} + +enum Field::DataTypes QueryResultSqlite::ConvertNativeType(int sqliteType) const +{ + switch (sqliteType) + { + case SQLITE_INTEGER: + return Field::DB_TYPE_INTEGER; + case SQLITE_FLOAT: + return Field::DB_TYPE_FLOAT; + case SQLITE_TEXT: + case SQLITE_BLOB: + case SQLITE_NULL: + return Field::DB_TYPE_STRING; + default: + return Field::DB_TYPE_UNKNOWN; + } +} +#endif diff --git a/src/shared/Database/QueryResultSqlite.h b/src/shared/Database/QueryResultSqlite.h new file mode 100644 index 00000000000..a9e84ef800b --- /dev/null +++ b/src/shared/Database/QueryResultSqlite.h @@ -0,0 +1,55 @@ +/* + * This file is part of the CMaNGOS Project. See AUTHORS file for Copyright information + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef DO_SQLITE + +#if !defined(QUERYRESULTSQLITE_H) +#define QUERYRESULTSQLITE_H + +#include "Common.h" +#include "QueryResult.h" + +#ifdef _WIN32 + #include +#endif + +#include + +class QueryResultSqlite : public QueryResult +{ + public: + QueryResultSqlite(sqlite3_stmt** stmt); + + ~QueryResultSqlite(); + + bool NextRow() override; + + operator bool() const + { + return (mRowCount * mFieldCount) > 0; + } + + private: + enum Field::DataTypes ConvertNativeType(int sqliteType) const; + //enum Field::DataTypes ConvertNativeType(enum_field_types mysqlType) const; + void EndQuery(); + + sqlite3_stmt** mStmt; +}; +#endif +#endif diff --git a/src/shared/Database/SqlDelayThread.cpp b/src/shared/Database/SqlDelayThread.cpp index 9e86d463e8a..bf4a0495fb7 100644 --- a/src/shared/Database/SqlDelayThread.cpp +++ b/src/shared/Database/SqlDelayThread.cpp @@ -33,7 +33,9 @@ SqlDelayThread::~SqlDelayThread() void SqlDelayThread::run() { #ifndef DO_POSTGRESQL +#ifndef DO_SQLITE mysql_thread_init(); +#endif #endif const uint32 loopSleepms = 10; @@ -57,8 +59,10 @@ void SqlDelayThread::run() } #ifndef DO_POSTGRESQL +#ifndef DO_SQLITE mysql_thread_end(); #endif +#endif } void SqlDelayThread::Stop()