db: prepare guild and rename queries
Some checks failed
build / Linux asan (push) Has been cancelled
build / Linux release (push) Has been cancelled
build / FreeBSD build (push) Has been cancelled

This commit is contained in:
server
2026-04-14 09:58:34 +02:00
parent 14e084d691
commit 58273bc116
7 changed files with 207 additions and 106 deletions

View File

@@ -7,12 +7,62 @@
#include "Config.h" #include "Config.h"
#include "QID.h" #include "QID.h"
#include "Cache.h" #include "Cache.h"
#include "libsql/Statement.h"
extern std::string g_stLocale; extern std::string g_stLocale;
extern bool CreatePlayerTableFromRes(MYSQL_RES * res, TPlayerTable * pkTab); extern bool CreatePlayerTableFromRes(MYSQL_RES * res, TPlayerTable * pkTab);
extern int g_test_server; extern int g_test_server;
extern int g_log; extern int g_log;
namespace
{
bool CountPlayersByNameExcludingId(const char* player_name, DWORD player_id, unsigned long long* count)
{
char query[QUERY_MAX_LEN];
if (g_stLocale == "sjis")
snprintf(query, sizeof(query), "SELECT COUNT(*) FROM player%s WHERE name = ? COLLATE sjis_japanese_ci AND id <> ?", GetTablePostfix());
else
snprintf(query, sizeof(query), "SELECT COUNT(*) FROM player%s WHERE name = ? AND id <> ?", GetTablePostfix());
CStmt stmt;
if (!stmt.Prepare(CDBManager::instance().GetDirectSQL(SQL_PLAYER), query))
return false;
if (!stmt.BindParam(MYSQL_TYPE_STRING, (void*) player_name, CHARACTER_NAME_MAX_LEN))
return false;
if (!stmt.BindParam(MYSQL_TYPE_LONG, &player_id))
return false;
if (!stmt.BindResult(MYSQL_TYPE_LONGLONG, count))
return false;
if (!stmt.Execute() || !stmt.Fetch())
return false;
return true;
}
bool UpdatePlayerName(DWORD player_id, const char* player_name)
{
char query[QUERY_MAX_LEN];
snprintf(query, sizeof(query), "UPDATE player%s SET name = ?, change_name = 0 WHERE id = ?", GetTablePostfix());
CStmt stmt;
if (!stmt.Prepare(CDBManager::instance().GetDirectSQL(SQL_PLAYER), query))
return false;
if (!stmt.BindParam(MYSQL_TYPE_STRING, (void*) player_name, CHARACTER_NAME_MAX_LEN))
return false;
if (!stmt.BindParam(MYSQL_TYPE_LONG, &player_id))
return false;
return stmt.Execute() != 0;
}
}
bool CClientManager::InsertLogonAccount(const char * c_pszLogin, DWORD dwHandle, const char * c_pszIP) bool CClientManager::InsertLogonAccount(const char * c_pszLogin, DWORD dwHandle, const char * c_pszIP)
{ {
char szLogin[LOGIN_MAX_LEN + 1]; char szLogin[LOGIN_MAX_LEN + 1];
@@ -464,44 +514,24 @@ void CClientManager::QUERY_LOGOUT(CPeer * peer, DWORD dwHandle,const char * data
void CClientManager::QUERY_CHANGE_NAME(CPeer * peer, DWORD dwHandle, TPacketGDChangeName * p) void CClientManager::QUERY_CHANGE_NAME(CPeer * peer, DWORD dwHandle, TPacketGDChangeName * p)
{ {
char queryStr[QUERY_MAX_LEN]; unsigned long long count = 0;
if (!CountPlayersByNameExcludingId(p->name, p->pid, &count))
if (g_stLocale == "sjis")
snprintf(queryStr, sizeof(queryStr),
"SELECT COUNT(*) as count FROM player%s WHERE name='%s' collate sjis_japanese_ci AND id <> %u",
GetTablePostfix(), p->name, p->pid);
else
snprintf(queryStr, sizeof(queryStr),
"SELECT COUNT(*) as count FROM player%s WHERE name='%s' AND id <> %u", GetTablePostfix(), p->name, p->pid);
auto pMsg = CDBManager::instance().DirectQuery(queryStr, SQL_PLAYER);
if (pMsg->Get()->uiNumRows)
{
if (!pMsg->Get()->pSQLResult)
{
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
return;
}
MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult);
if (*row[0] != '0')
{
peer->EncodeHeader(DG::PLAYER_CREATE_ALREADY, dwHandle, 0);
return;
}
}
else
{ {
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0); peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
return; return;
} }
snprintf(queryStr, sizeof(queryStr), if (count != 0)
"UPDATE player%s SET name='%s',change_name=0 WHERE id=%u", GetTablePostfix(), p->name, p->pid); {
peer->EncodeHeader(DG::PLAYER_CREATE_ALREADY, dwHandle, 0);
return;
}
auto pMsg0 = CDBManager::instance().DirectQuery(queryStr, SQL_PLAYER); if (!UpdatePlayerName(p->pid, p->name))
{
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
return;
}
TPacketDGChangeName pdg; TPacketDGChangeName pdg;
peer->EncodeHeader(DG::CHANGE_NAME, dwHandle, sizeof(TPacketDGChangeName)); peer->EncodeHeader(DG::CHANGE_NAME, dwHandle, sizeof(TPacketDGChangeName));
@@ -509,4 +539,3 @@ void CClientManager::QUERY_CHANGE_NAME(CPeer * peer, DWORD dwHandle, TPacketGDCh
strlcpy(pdg.name, p->name, sizeof(pdg.name)); strlcpy(pdg.name, p->name, sizeof(pdg.name));
peer->Encode(&pdg, sizeof(TPacketDGChangeName)); peer->Encode(&pdg, sizeof(TPacketDGChangeName));
} }

View File

@@ -43,6 +43,7 @@ class CDBManager : public singleton<CDBManager>
void ReturnQuery(const char * c_pszQuery, int iType, IDENT dwIdent, void * pvData, int iSlot = SQL_PLAYER); void ReturnQuery(const char * c_pszQuery, int iType, IDENT dwIdent, void * pvData, int iSlot = SQL_PLAYER);
void AsyncQuery(const char * c_pszQuery, int iSlot = SQL_PLAYER); void AsyncQuery(const char * c_pszQuery, int iSlot = SQL_PLAYER);
std::unique_ptr<SQLMsg> DirectQuery(const char* c_pszQuery, int iSlot = SQL_PLAYER); std::unique_ptr<SQLMsg> DirectQuery(const char* c_pszQuery, int iSlot = SQL_PLAYER);
CAsyncSQL* GetDirectSQL(int iSlot = SQL_PLAYER) { return m_directSQL[iSlot].get(); }
SQLMsg * PopResult(); SQLMsg * PopResult();
SQLMsg * PopResult(eSQL_SLOT slot ); SQLMsg * PopResult(eSQL_SLOT slot );

View File

@@ -74,6 +74,7 @@ class DBManager : public singleton<DBManager>
std::unique_ptr<SQLMsg> DirectQuery(const char* c_pszFormat, ...); std::unique_ptr<SQLMsg> DirectQuery(const char* c_pszFormat, ...);
void ReturnQuery(int iType, DWORD dwIdent, void* pvData, const char * c_pszFormat, ...); void ReturnQuery(int iType, DWORD dwIdent, void* pvData, const char * c_pszFormat, ...);
CAsyncSQL* GetDirectSQL() { return &m_sql_direct; }
void Process(); void Process();
void AnalyzeReturnQuery(SQLMsg * pmsg); void AnalyzeReturnQuery(SQLMsg * pmsg);

View File

@@ -15,9 +15,30 @@
#include "locale_service.h" #include "locale_service.h"
#include "guild_manager.h" #include "guild_manager.h"
#include "MarkManager.h" #include "MarkManager.h"
#include "libsql/Statement.h"
namespace namespace
{ {
bool CountGuildsByName(const char* guild_name, unsigned long long* count)
{
char query[256];
snprintf(query, sizeof(query), "SELECT COUNT(*) FROM guild%s WHERE name = ?", get_table_postfix());
CStmt stmt;
if (!stmt.Prepare(DBManager::instance().GetDirectSQL(), query))
return false;
if (!stmt.BindParam(MYSQL_TYPE_STRING, (void*) guild_name, GUILD_NAME_MAX_LEN))
return false;
if (!stmt.BindResult(MYSQL_TYPE_LONGLONG, count))
return false;
if (!stmt.Execute() || !stmt.Fetch())
return false;
return true;
}
struct FGuildNameSender struct FGuildNameSender
{ {
@@ -81,25 +102,19 @@ DWORD CGuildManager::CreateGuild(TGuildCreateParameter& gcp)
return 0; return 0;
} }
auto pmsg = DBManager::instance().DirectQuery("SELECT COUNT(*) FROM guild%s WHERE name = '%s'", unsigned long long guild_count = 0;
get_table_postfix(), gcp.name); if (!CountGuildsByName(gcp.name, &guild_count))
if (pmsg->Get()->uiNumRows > 0)
{
MYSQL_ROW row = mysql_fetch_row(pmsg->Get()->pSQLResult);
if (!(row[0] && row[0][0] == '0'))
{
gcp.master->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 이미 같은 이름의 길드가 있습니다."));
return 0;
}
}
else
{ {
gcp.master->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 길드를 생성할 수 없습니다.")); gcp.master->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 길드를 생성할 수 없습니다."));
return 0; return 0;
} }
if (guild_count != 0)
{
gcp.master->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 이미 같은 이름의 길드가 있습니다."));
return 0;
}
// new CGuild(gcp) queries guild tables and tell dbcache to notice other game servers. // new CGuild(gcp) queries guild tables and tell dbcache to notice other game servers.
// other game server calls CGuildManager::LoadGuild to load guild. // other game server calls CGuildManager::LoadGuild to load guild.
CGuild * pg = M2_NEW CGuild(gcp); CGuild * pg = M2_NEW CGuild(gcp);
@@ -956,4 +971,3 @@ void CGuildManager::ChangeMaster(DWORD dwGID)
"SELECT 1"); "SELECT 1");
} }

View File

@@ -22,6 +22,7 @@
#include "utils.h" #include "utils.h"
#include "unique_item.h" #include "unique_item.h"
#include "mob_manager.h" #include "mob_manager.h"
#include "libsql/Statement.h"
#include <cctype> #include <cctype>
#undef sys_err #undef sys_err
@@ -33,6 +34,48 @@ const int ITEM_BROKEN_METIN_VNUM = 28960;
namespace quest namespace quest
{ {
namespace
{
bool CountPlayersByName(const char* player_name, unsigned long long* count)
{
char query[256];
snprintf(query, sizeof(query), "SELECT COUNT(*) FROM player%s WHERE name = ?", get_table_postfix());
CStmt stmt;
if (!stmt.Prepare(DBManager::instance().GetDirectSQL(), query))
return false;
if (!stmt.BindParam(MYSQL_TYPE_STRING, (void*) player_name, CHARACTER_NAME_MAX_LEN))
return false;
if (!stmt.BindResult(MYSQL_TYPE_LONGLONG, count))
return false;
if (!stmt.Execute() || !stmt.Fetch())
return false;
return true;
}
bool UpdatePlayerName(DWORD player_id, const char* player_name)
{
char query[256];
snprintf(query, sizeof(query), "UPDATE player%s SET name = ?, change_name = 0 WHERE id = ?", get_table_postfix());
CStmt stmt;
if (!stmt.Prepare(DBManager::instance().GetDirectSQL(), query))
return false;
if (!stmt.BindParam(MYSQL_TYPE_STRING, (void*) player_name, CHARACTER_NAME_MAX_LEN))
return false;
if (!stmt.BindParam(MYSQL_TYPE_LONG, &player_id))
return false;
return stmt.Execute() != 0;
}
}
// //
// "pc" Lua functions // "pc" Lua functions
// //
@@ -2114,23 +2157,18 @@ teleport_area:
return 1; return 1;
} }
char szQuery[1024]; unsigned long long count = 0;
snprintf(szQuery, sizeof(szQuery), "SELECT COUNT(*) FROM player%s WHERE name='%s'", get_table_postfix(), szName); if (!CountPlayersByName(szName, &count))
auto pmsg = DBManager::instance().DirectQuery(szQuery);
if ( pmsg->Get()->uiNumRows > 0 )
{ {
MYSQL_ROW row = mysql_fetch_row(pmsg->Get()->pSQLResult); lua_pushnumber(L, 5);
return 1;
}
int count = 0; // 이미 해당 이름을 가진 캐릭터가 있음
str_to_number(count, row[0]); if (count != 0)
{
// 이미 해당 이름을 가진 캐릭터가 있음 lua_pushnumber(L, 3);
if ( count != 0 ) return 1;
{
lua_pushnumber(L, 3);
return 1;
}
} }
DWORD pid = ch->GetPlayerID(); DWORD pid = ch->GetPlayerID();
@@ -2144,8 +2182,11 @@ teleport_area:
/* change_name_log */ /* change_name_log */
LogManager::instance().ChangeNameLog(pid, ch->GetName(), szName, ch->GetDesc()->GetHostName()); LogManager::instance().ChangeNameLog(pid, ch->GetName(), szName, ch->GetDesc()->GetHostName());
snprintf(szQuery, sizeof(szQuery), "UPDATE player%s SET name='%s' WHERE id=%u", get_table_postfix(), szName, pid); if (!UpdatePlayerName(pid, szName))
DBManager::instance().DirectQuery(szQuery); {
lua_pushnumber(L, 5);
return 1;
}
ch->SetNewName(szName); ch->SetNewName(szName);
lua_pushnumber(L, 4); lua_pushnumber(L, 4);

View File

@@ -9,7 +9,6 @@ CStmt::CStmt()
m_uiParamCount = 0; m_uiParamCount = 0;
m_uiResultCount = 0; m_uiResultCount = 0;
iRows = 0; iRows = 0;
m_puiParamLen = NULL;
} }
CStmt::~CStmt() CStmt::~CStmt()
@@ -25,11 +24,13 @@ void CStmt::Destroy()
m_pkStmt = NULL; m_pkStmt = NULL;
} }
if (m_puiParamLen) m_vec_param.clear();
{ m_vecParamLen.clear();
free(m_puiParamLen); m_vec_result.clear();
m_puiParamLen = NULL; m_vecResultLen.clear();
} m_uiParamCount = 0;
m_uiResultCount = 0;
iRows = 0;
} }
void CStmt::Error(const char * c_pszMsg) void CStmt::Error(const char * c_pszMsg)
@@ -39,6 +40,7 @@ void CStmt::Error(const char * c_pszMsg)
bool CStmt::Prepare(CAsyncSQL * sql, const char * c_pszQuery) bool CStmt::Prepare(CAsyncSQL * sql, const char * c_pszQuery)
{ {
Destroy();
m_pkStmt = mysql_stmt_init(sql->GetSQLHandle()); m_pkStmt = mysql_stmt_init(sql->GetSQLHandle());
m_stQuery = c_pszQuery; m_stQuery = c_pszQuery;
@@ -48,27 +50,20 @@ bool CStmt::Prepare(CAsyncSQL * sql, const char * c_pszQuery)
return false; return false;
} }
int iParamCount = 0; const unsigned int param_count = mysql_stmt_param_count(m_pkStmt);
if (param_count)
for (unsigned int i = 0; i < m_stQuery.length(); ++i)
if (c_pszQuery[i] == '?')
++iParamCount;
if (iParamCount)
{ {
m_vec_param.resize(iParamCount); m_vec_param.resize(param_count);
memset(&m_vec_param[0], 0, sizeof(MYSQL_BIND) * iParamCount); memset(&m_vec_param[0], 0, sizeof(MYSQL_BIND) * param_count);
m_vecParamLen.resize(param_count, 0);
m_puiParamLen = (long unsigned int *) calloc(iParamCount, sizeof(long unsigned int));
} }
m_vec_result.resize(48); const unsigned int result_count = mysql_stmt_field_count(m_pkStmt);
memset(&m_vec_result[0], 0, sizeof(MYSQL_BIND) * 48); if (result_count)
if (mysql_stmt_bind_result(m_pkStmt, &m_vec_result[0]))
{ {
Error("mysql_stmt_bind_result"); m_vec_result.resize(result_count);
return 0; memset(&m_vec_result[0], 0, sizeof(MYSQL_BIND) * result_count);
m_vecResultLen.resize(result_count, 0);
} }
return true; return true;
@@ -87,16 +82,8 @@ bool CStmt::BindParam(enum_field_types type, void * p, int iMaxLen)
bind->buffer_type = type; bind->buffer_type = type;
bind->buffer = (void *) p; bind->buffer = (void *) p;
bind->buffer_length = iMaxLen; bind->buffer_length = iMaxLen;
bind->length = m_puiParamLen + m_uiParamCount; bind->length = m_vecParamLen.empty() ? NULL : &m_vecParamLen[m_uiParamCount];
++m_uiParamCount;
if (++m_uiParamCount == m_vec_param.size())
{
if (mysql_stmt_bind_param(m_pkStmt, &m_vec_param[0]))
{
Error("mysql_stmt_bind_param");
return false;
}
}
return true; return true;
} }
@@ -114,6 +101,7 @@ bool CStmt::BindResult(enum_field_types type, void * p, int iMaxLen)
bind->buffer_type = type; bind->buffer_type = type;
bind->buffer = (void *) p; bind->buffer = (void *) p;
bind->buffer_length = iMaxLen; bind->buffer_length = iMaxLen;
bind->length = m_vecResultLen.empty() ? NULL : &m_vecResultLen[m_uiResultCount - 1];
return true; return true;
} }
@@ -131,9 +119,18 @@ int CStmt::Execute()
if (bind->buffer_type == MYSQL_TYPE_STRING) if (bind->buffer_type == MYSQL_TYPE_STRING)
{ {
*(m_puiParamLen + i) = strlen((const char *) bind->buffer); m_vecParamLen[i] = strlen((const char *) bind->buffer);
sys_log(0, "param %d len %d buf %s", i, *m_puiParamLen, (const char *) bind->buffer);
} }
else if (bind->buffer_type == MYSQL_TYPE_BLOB)
{
m_vecParamLen[i] = bind->buffer_length;
}
}
if (!m_vec_param.empty() && mysql_stmt_bind_param(m_pkStmt, &m_vec_param[0]))
{
Error("mysql_stmt_bind_param");
return 0;
} }
if (mysql_stmt_execute(m_pkStmt)) if (mysql_stmt_execute(m_pkStmt))
@@ -142,13 +139,31 @@ int CStmt::Execute()
return 0; return 0;
} }
if (mysql_stmt_store_result(m_pkStmt)) if (!m_vec_result.empty())
{ {
Error("mysql_store_result"); if (m_uiResultCount != m_vec_result.size())
return 0; {
sys_log(0, "Result count mismatch %u, expected %zu query: %s", m_uiResultCount, m_vec_result.size(), m_stQuery.c_str());
return 0;
}
if (mysql_stmt_bind_result(m_pkStmt, &m_vec_result[0]))
{
Error("mysql_stmt_bind_result");
return 0;
}
if (mysql_stmt_store_result(m_pkStmt))
{
Error("mysql_stmt_store_result");
return 0;
}
iRows = mysql_stmt_num_rows(m_pkStmt);
return true;
} }
iRows = mysql_stmt_num_rows(m_pkStmt); iRows = 0;
return true; return true;
} }
@@ -156,4 +171,3 @@ bool CStmt::Fetch()
{ {
return !mysql_stmt_fetch(m_pkStmt); return !mysql_stmt_fetch(m_pkStmt);
} }

View File

@@ -32,10 +32,11 @@ class CStmt
std::vector<MYSQL_BIND> m_vec_param; std::vector<MYSQL_BIND> m_vec_param;
unsigned int m_uiParamCount; unsigned int m_uiParamCount;
long unsigned int * m_puiParamLen; std::vector<unsigned long> m_vecParamLen;
std::vector<MYSQL_BIND> m_vec_result; std::vector<MYSQL_BIND> m_vec_result;
unsigned int m_uiResultCount; unsigned int m_uiResultCount;
std::vector<unsigned long> m_vecResultLen;
}; };
#endif #endif