2020-07-04 08:13:21 +00:00
|
|
|
#include "mysql.h"
|
|
|
|
|
2020-07-04 17:53:27 +00:00
|
|
|
#if defined(CONF_SQL)
|
2020-07-04 08:13:21 +00:00
|
|
|
#include <cppconn/driver.h>
|
2020-07-04 17:53:27 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
lock CMysqlConnection::m_SqlDriverLock;
|
2020-07-04 08:13:21 +00:00
|
|
|
|
|
|
|
CMysqlConnection::CMysqlConnection(
|
|
|
|
const char *pDatabase,
|
|
|
|
const char *pPrefix,
|
|
|
|
const char *pUser,
|
|
|
|
const char *pPass,
|
|
|
|
const char *pIp,
|
|
|
|
int Port,
|
|
|
|
bool Setup) :
|
|
|
|
IDbConnection(pPrefix),
|
2020-07-04 17:53:27 +00:00
|
|
|
#if defined(CONF_SQL)
|
|
|
|
m_NewQuery(false),
|
|
|
|
m_Locked(false),
|
|
|
|
#endif
|
2020-07-04 08:13:21 +00:00
|
|
|
m_Port(Port),
|
|
|
|
m_Setup(Setup),
|
|
|
|
m_InUse(false)
|
|
|
|
{
|
|
|
|
str_copy(m_aDatabase, pDatabase, sizeof(m_aDatabase));
|
|
|
|
str_copy(m_aUser, pUser, sizeof(m_aUser));
|
|
|
|
str_copy(m_aPass, pPass, sizeof(m_aPass));
|
|
|
|
str_copy(m_aIp, pIp, sizeof(m_aIp));
|
2020-07-04 17:53:27 +00:00
|
|
|
#if not defined(CONF_SQL)
|
|
|
|
dbg_msg("sql", "Adding MySQL server failed due to MySQL support not enabled during compile time");
|
|
|
|
#endif
|
2020-07-04 08:13:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CMysqlConnection::~CMysqlConnection()
|
|
|
|
{
|
2020-07-10 23:28:37 +00:00
|
|
|
#if defined(CONF_SQL)
|
|
|
|
m_pStmt.release();
|
|
|
|
m_pPreparedStmt.release();
|
|
|
|
m_pConnection.release();
|
|
|
|
#endif
|
2020-07-04 08:13:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CMysqlConnection *CMysqlConnection::Copy()
|
|
|
|
{
|
2020-07-09 17:02:28 +00:00
|
|
|
return new CMysqlConnection(m_aDatabase, GetPrefix(), m_aUser, m_aPass, m_aIp, m_Port, m_Setup);
|
2020-07-04 08:13:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
IDbConnection::Status CMysqlConnection::Connect()
|
|
|
|
{
|
2020-07-04 17:53:27 +00:00
|
|
|
#if defined(CONF_SQL)
|
2020-07-04 08:13:21 +00:00
|
|
|
if(m_InUse.exchange(true))
|
|
|
|
return Status::IN_USE;
|
|
|
|
|
2020-07-04 17:53:27 +00:00
|
|
|
m_NewQuery = true;
|
|
|
|
if(m_pConnection != nullptr)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// Connect to specific database
|
|
|
|
m_pConnection->setSchema(m_aDatabase);
|
|
|
|
return Status::SUCCESS;
|
|
|
|
}
|
|
|
|
catch (sql::SQLException &e)
|
|
|
|
{
|
|
|
|
dbg_msg("sql", "MySQL Error: %s", e.what());
|
|
|
|
}
|
|
|
|
catch (const std::exception& ex)
|
|
|
|
{
|
|
|
|
dbg_msg("sql", "MySQL Error: %s", ex.what());
|
|
|
|
}
|
|
|
|
catch (const std::string& ex)
|
|
|
|
{
|
|
|
|
dbg_msg("sql", "MySQL Error: %s", ex.c_str());
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
dbg_msg("sql", "Unknown Error cause by the MySQL/C++ Connector");
|
|
|
|
}
|
|
|
|
|
2020-07-14 14:59:09 +00:00
|
|
|
dbg_msg("sql", "ERROR: SQL connection failed, trying to reconnect");
|
2020-07-04 17:53:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
m_pConnection.release();
|
|
|
|
m_pPreparedStmt.release();
|
|
|
|
m_pResults.release();
|
|
|
|
|
|
|
|
sql::ConnectOptionsMap connection_properties;
|
|
|
|
connection_properties["hostName"] = sql::SQLString(m_aIp);
|
|
|
|
connection_properties["port"] = m_Port;
|
|
|
|
connection_properties["userName"] = sql::SQLString(m_aUser);
|
|
|
|
connection_properties["password"] = sql::SQLString(m_aPass);
|
|
|
|
connection_properties["OPT_CONNECT_TIMEOUT"] = 10;
|
|
|
|
connection_properties["OPT_READ_TIMEOUT"] = 10;
|
|
|
|
connection_properties["OPT_WRITE_TIMEOUT"] = 20;
|
|
|
|
connection_properties["OPT_RECONNECT"] = true;
|
|
|
|
connection_properties["OPT_CHARSET_NAME"] = sql::SQLString("utf8mb4");
|
|
|
|
connection_properties["OPT_SET_CHARSET_NAME"] = sql::SQLString("utf8mb4");
|
|
|
|
|
|
|
|
// Create connection
|
|
|
|
{
|
|
|
|
scope_lock GlobalLockScope(&m_SqlDriverLock);
|
|
|
|
sql::Driver *pDriver = get_driver_instance();
|
|
|
|
m_pConnection.reset(pDriver->connect(connection_properties));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create Statement
|
|
|
|
m_pStmt = std::unique_ptr<sql::Statement>(m_pConnection->createStatement());
|
|
|
|
|
|
|
|
// Apparently OPT_CHARSET_NAME and OPT_SET_CHARSET_NAME are not enough
|
|
|
|
m_pStmt->execute("SET CHARACTER SET utf8mb4;");
|
|
|
|
|
|
|
|
if(m_Setup)
|
|
|
|
{
|
|
|
|
char aBuf[1024];
|
|
|
|
// create database
|
|
|
|
str_format(aBuf, sizeof(aBuf), "CREATE DATABASE IF NOT EXISTS %s CHARACTER SET utf8mb4", m_aDatabase);
|
|
|
|
m_pStmt->execute(aBuf);
|
|
|
|
// Connect to specific database
|
|
|
|
m_pConnection->setSchema(m_aDatabase);
|
2020-07-09 17:02:28 +00:00
|
|
|
FormatCreateRace(aBuf, sizeof(aBuf));
|
2020-07-04 17:53:27 +00:00
|
|
|
m_pStmt->execute(aBuf);
|
2020-07-09 17:18:18 +00:00
|
|
|
FormatCreateTeamrace(aBuf, sizeof(aBuf), "VARBINARY(16)");
|
2020-07-04 17:53:27 +00:00
|
|
|
m_pStmt->execute(aBuf);
|
2020-07-09 17:02:28 +00:00
|
|
|
FormatCreateMaps(aBuf, sizeof(aBuf));
|
2020-07-04 17:53:27 +00:00
|
|
|
m_pStmt->execute(aBuf);
|
2020-07-09 17:02:28 +00:00
|
|
|
FormatCreateSaves(aBuf, sizeof(aBuf));
|
2020-07-04 17:53:27 +00:00
|
|
|
m_pStmt->execute(aBuf);
|
2020-07-09 17:02:28 +00:00
|
|
|
FormatCreatePoints(aBuf, sizeof(aBuf));
|
2020-07-04 17:53:27 +00:00
|
|
|
m_pStmt->execute(aBuf);
|
|
|
|
m_Setup = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Connect to specific database
|
|
|
|
m_pConnection->setSchema(m_aDatabase);
|
|
|
|
}
|
|
|
|
dbg_msg("sql", "sql connection established");
|
|
|
|
return Status::SUCCESS;
|
|
|
|
}
|
|
|
|
catch (sql::SQLException &e)
|
|
|
|
{
|
|
|
|
dbg_msg("sql", "MySQL Error: %s", e.what());
|
|
|
|
}
|
|
|
|
catch (const std::exception& ex)
|
|
|
|
{
|
|
|
|
dbg_msg("sql", "MySQL Error: %s", ex.what());
|
|
|
|
}
|
|
|
|
catch (const std::string& ex)
|
|
|
|
{
|
|
|
|
dbg_msg("sql", "MySQL Error: %s", ex.c_str());
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
dbg_msg("sql", "Unknown Error cause by the MySQL/C++ Connector");
|
|
|
|
}
|
2020-07-14 14:59:09 +00:00
|
|
|
m_InUse.store(false);
|
2020-07-04 17:53:27 +00:00
|
|
|
|
|
|
|
#endif
|
|
|
|
dbg_msg("sql", "ERROR: sql connection failed");
|
2020-07-04 08:13:21 +00:00
|
|
|
return Status::ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CMysqlConnection::Disconnect()
|
|
|
|
{
|
|
|
|
m_InUse.store(false);
|
|
|
|
}
|
|
|
|
|
2020-07-04 17:53:27 +00:00
|
|
|
void CMysqlConnection::Lock(const char *pTable)
|
2020-07-04 08:13:21 +00:00
|
|
|
{
|
2020-07-04 17:53:27 +00:00
|
|
|
#if defined(CONF_SQL)
|
|
|
|
char aBuf[512];
|
|
|
|
str_format(aBuf, sizeof(aBuf), "lock tables %s write;", pTable);
|
|
|
|
m_pStmt->execute(aBuf);
|
|
|
|
m_Locked = true;
|
|
|
|
#endif
|
2020-07-04 08:13:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CMysqlConnection::Unlock()
|
|
|
|
{
|
2020-07-04 17:53:27 +00:00
|
|
|
#if defined(CONF_SQL)
|
|
|
|
if(m_Locked)
|
|
|
|
{
|
|
|
|
m_pStmt->execute("unlock tables;");
|
|
|
|
m_Locked = false;
|
|
|
|
}
|
|
|
|
#endif
|
2020-07-04 08:13:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CMysqlConnection::PrepareStatement(const char *pStmt)
|
|
|
|
{
|
2020-07-04 17:53:27 +00:00
|
|
|
#if defined(CONF_SQL)
|
|
|
|
m_pPreparedStmt.reset(m_pConnection->prepareStatement(pStmt));
|
|
|
|
m_NewQuery = true;
|
|
|
|
#endif
|
2020-07-04 08:13:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CMysqlConnection::BindString(int Idx, const char *pString)
|
|
|
|
{
|
2020-07-04 17:53:27 +00:00
|
|
|
#if defined(CONF_SQL)
|
|
|
|
m_pPreparedStmt->setString(Idx, pString);
|
|
|
|
m_NewQuery = true;
|
|
|
|
#endif
|
2020-07-04 08:13:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CMysqlConnection::BindInt(int Idx, int Value)
|
|
|
|
{
|
2020-07-04 17:53:27 +00:00
|
|
|
#if defined(CONF_SQL)
|
|
|
|
m_pPreparedStmt->setInt(Idx, Value);
|
|
|
|
m_NewQuery = true;
|
|
|
|
#endif
|
2020-07-04 08:13:21 +00:00
|
|
|
}
|
|
|
|
|
2020-07-04 17:53:27 +00:00
|
|
|
bool CMysqlConnection::Step()
|
2020-07-04 08:13:21 +00:00
|
|
|
{
|
2020-07-04 17:53:27 +00:00
|
|
|
#if defined(CONF_SQL)
|
|
|
|
if(m_NewQuery)
|
|
|
|
{
|
|
|
|
m_NewQuery = false;
|
|
|
|
m_pResults.reset(m_pPreparedStmt->executeQuery());
|
|
|
|
}
|
|
|
|
return m_pResults->next();
|
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
2020-07-04 08:13:21 +00:00
|
|
|
}
|
|
|
|
|
2020-07-04 17:53:27 +00:00
|
|
|
bool CMysqlConnection::IsNull(int Col) const
|
2020-07-04 08:13:21 +00:00
|
|
|
{
|
2020-07-04 17:53:27 +00:00
|
|
|
#if defined(CONF_SQL)
|
|
|
|
return m_pResults->isNull(Col);
|
|
|
|
#else
|
2020-07-04 08:13:21 +00:00
|
|
|
return false;
|
2020-07-04 17:53:27 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
float CMysqlConnection::GetFloat(int Col) const
|
|
|
|
{
|
|
|
|
#if defined(CONF_SQL)
|
|
|
|
return (float)m_pResults->getDouble(Col);
|
|
|
|
#else
|
|
|
|
return 0.0;
|
|
|
|
#endif
|
2020-07-04 08:13:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int CMysqlConnection::GetInt(int Col) const
|
|
|
|
{
|
2020-07-04 17:53:27 +00:00
|
|
|
#if defined(CONF_SQL)
|
|
|
|
return m_pResults->getInt(Col);
|
|
|
|
#else
|
2020-07-04 08:13:21 +00:00
|
|
|
return 0;
|
2020-07-04 17:53:27 +00:00
|
|
|
#endif
|
2020-07-04 08:13:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CMysqlConnection::GetString(int Col, char *pBuffer, int BufferSize) const
|
|
|
|
{
|
2020-07-04 17:53:27 +00:00
|
|
|
#if defined(CONF_SQL)
|
|
|
|
auto String = m_pResults->getString(Col);
|
|
|
|
str_copy(pBuffer, String.c_str(), BufferSize);
|
|
|
|
#endif
|
2020-07-04 08:13:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int CMysqlConnection::GetBlob(int Col, unsigned char *pBuffer, int BufferSize) const
|
|
|
|
{
|
2020-07-04 17:53:27 +00:00
|
|
|
#if defined(CONF_SQL)
|
|
|
|
auto Blob = m_pResults->getBlob(Col);
|
|
|
|
Blob->read((char *)pBuffer, BufferSize);
|
|
|
|
return Blob->gcount();
|
|
|
|
#else
|
2020-07-04 08:13:21 +00:00
|
|
|
return 0;
|
2020-07-04 17:53:27 +00:00
|
|
|
#endif
|
2020-07-04 08:13:21 +00:00
|
|
|
}
|
2020-07-12 12:07:12 +00:00
|
|
|
|
|
|
|
void CMysqlConnection::AddPoints(const char *pPlayer, int Points)
|
|
|
|
{
|
|
|
|
char aBuf[512];
|
|
|
|
str_format(aBuf, sizeof(aBuf),
|
|
|
|
"INSERT INTO %s_points(Name, Points) "
|
|
|
|
"VALUES (?, ?) "
|
|
|
|
"ON DUPLICATE KEY UPDATE Points=Points+?;",
|
|
|
|
GetPrefix());
|
|
|
|
PrepareStatement(aBuf);
|
|
|
|
BindString(1, pPlayer);
|
|
|
|
BindInt(2, Points);
|
|
|
|
BindInt(3, Points);
|
|
|
|
Step();
|
|
|
|
}
|