mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 01:58:19 +00:00
Get rid of the MySQL C++ connector as a dependency
We now use the C API directly. This has the advantage of using one obscure dependency less, as the C++ connector also used the C API.
This commit is contained in:
parent
a1fb94c6b7
commit
d7019a244e
2
.github/workflows/build.yaml
vendored
2
.github/workflows/build.yaml
vendored
|
@ -63,7 +63,7 @@ jobs:
|
|||
- name: Prepare Linux (fancy)
|
||||
if: contains(matrix.os, 'ubuntu') && matrix.fancy
|
||||
run: |
|
||||
sudo apt-get install libboost-dev libmariadbclient-dev libmysqlcppconn-dev libwebsockets-dev -y
|
||||
sudo apt-get install libmariadbclient-dev libwebsockets-dev -y
|
||||
|
||||
- name: Prepare MacOS
|
||||
if: contains(matrix.os, 'macOS')
|
||||
|
|
|
@ -1964,9 +1964,7 @@ set_src(ENGINE_SERVER GLOB_RECURSE src/engine/server
|
|||
databases/connection_pool.cpp
|
||||
databases/connection_pool.h
|
||||
databases/mysql.cpp
|
||||
databases/mysql.h
|
||||
databases/sqlite.cpp
|
||||
databases/sqlite.h
|
||||
name_ban.cpp
|
||||
name_ban.h
|
||||
register.cpp
|
||||
|
|
|
@ -39,30 +39,13 @@ endif()
|
|||
set_extra_dirs_lib(MYSQL mysql)
|
||||
find_library(MYSQL_LIBRARY
|
||||
NAMES "mysqlclient" "mysqlclient_r" "mariadbclient"
|
||||
HINTS ${HINTS_MYSQL_LIBDIR} ${MYSQL_CONFIG_LIBRARY_PATH}
|
||||
PATHS ${PATHS_MYSQL_LIBDIR}
|
||||
HINTS ${MYSQL_CONFIG_LIBRARY_PATH}
|
||||
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
|
||||
)
|
||||
set_extra_dirs_include(MYSQL mysql "${MYSQL_LIBRARY}")
|
||||
find_path(MYSQL_INCLUDEDIR
|
||||
NAMES "mysql.h"
|
||||
HINTS ${HINTS_MYSQL_INCLUDEDIR} ${MYSQL_CONFIG_INCLUDE_DIR}
|
||||
PATHS ${PATHS_MYSQL_INCLUDEDIR}
|
||||
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
|
||||
)
|
||||
|
||||
set_extra_dirs_lib(MYSQL_CPPCONN mysql)
|
||||
find_library(MYSQL_CPPCONN_LIBRARY
|
||||
NAMES "mysqlcppconn" "mysqlcppconn-static"
|
||||
HINTS ${HINTS_MYSQL_CPPCONN_LIBDIR} ${MYSQL_CONFIG_LIBRARY_PATH}
|
||||
PATHS ${PATHS_MYSQL_CPPCONN_LIBDIR}
|
||||
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
|
||||
)
|
||||
set_extra_dirs_include(MYSQL_CPPCONN mysql "${MYSQL_CPPCONN_LIBRARY}")
|
||||
find_path(MYSQL_CPPCONN_INCLUDEDIR
|
||||
NAMES "mysql_connection.h"
|
||||
HINTS ${HINTS_MYSQL_CPPCONN_INCLUDEDIR} ${MYSQL_CONFIG_INCLUDE_DIR}
|
||||
PATHS ${PATHS_MYSQL_CPPCONN_INCLUDEDIR}
|
||||
HINTS ${MYSQL_CONFIG_INCLUDE_DIR}
|
||||
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
|
||||
)
|
||||
|
||||
|
@ -70,10 +53,8 @@ include(FindPackageHandleStandardArgs)
|
|||
find_package_handle_standard_args(MySQL DEFAULT_MSG MYSQL_LIBRARY MYSQL_INCLUDEDIR)
|
||||
|
||||
if(MYSQL_FOUND)
|
||||
is_bundled(MYSQL_BUNDLED "${MYSQL_LIBRARY}")
|
||||
|
||||
set(MYSQL_LIBRARIES ${MYSQL_LIBRARY} ${MYSQL_CPPCONN_LIBRARY})
|
||||
set(MYSQL_INCLUDE_DIRS ${MYSQL_INCLUDEDIR} ${MYSQL_CPPCONN_INCLUDEDIR})
|
||||
set(MYSQL_LIBRARIES ${MYSQL_LIBRARY})
|
||||
set(MYSQL_INCLUDE_DIRS ${MYSQL_INCLUDEDIR})
|
||||
|
||||
mark_as_advanced(MYSQL_INCLUDEDIR MYSQL_LIBRARY)
|
||||
endif()
|
||||
|
|
|
@ -64,16 +64,15 @@ public:
|
|||
// when called multiple times the next row is selected
|
||||
virtual bool Step() = 0;
|
||||
// executes the query and returns the number of rows affected by the update/insert/delete
|
||||
// FIXME(2020-01-20): change function to AffectedRows() when moved to c-api of MySQL
|
||||
virtual int ExecuteUpdate() = 0;
|
||||
|
||||
virtual bool IsNull(int Col) const = 0;
|
||||
virtual float GetFloat(int Col) const = 0;
|
||||
virtual int GetInt(int Col) const = 0;
|
||||
virtual bool IsNull(int Col) = 0;
|
||||
virtual float GetFloat(int Col) = 0;
|
||||
virtual int GetInt(int Col) = 0;
|
||||
// ensures that the string is null terminated
|
||||
virtual void GetString(int Col, char *pBuffer, int BufferSize) const = 0;
|
||||
virtual void GetString(int Col, char *pBuffer, int BufferSize) = 0;
|
||||
// returns number of bytes read into the buffer
|
||||
virtual int GetBlob(int Col, unsigned char *pBuffer, int BufferSize) const = 0;
|
||||
virtual int GetBlob(int Col, unsigned char *pBuffer, int BufferSize) = 0;
|
||||
|
||||
// SQL statements, that can't be abstracted, has side effects to the result
|
||||
virtual void AddPoints(const char *pPlayer, int Points) = 0;
|
||||
|
@ -89,4 +88,18 @@ protected:
|
|||
void FormatCreatePoints(char *aBuf, unsigned int BufferSize);
|
||||
};
|
||||
|
||||
int MysqlInit();
|
||||
void MysqlUninit();
|
||||
|
||||
IDbConnection *CreateSqliteConnection(const char *pFilename, bool Setup);
|
||||
// Returns nullptr if MySQL support is not compiled in.
|
||||
IDbConnection *CreateMysqlConnection(
|
||||
const char *pDatabase,
|
||||
const char *pPrefix,
|
||||
const char *pUser,
|
||||
const char *pPass,
|
||||
const char *pIp,
|
||||
int Port,
|
||||
bool Setup);
|
||||
|
||||
#endif // ENGINE_SERVER_DATABASES_CONNECTION_H
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
#include "connection.h"
|
||||
|
||||
#include <engine/console.h>
|
||||
#if defined(CONF_SQL)
|
||||
#include <cppconn/exception.h>
|
||||
#endif
|
||||
#include <stdexcept>
|
||||
|
||||
// helper struct to hold thread data
|
||||
|
@ -223,12 +220,6 @@ bool CDbConnectionPool::ExecSqlFunc(IDbConnection *pConnection, CSqlExecData *pD
|
|||
break;
|
||||
}
|
||||
}
|
||||
#if defined(CONF_SQL)
|
||||
catch(sql::SQLException &e)
|
||||
{
|
||||
dbg_msg("sql", "%s MySQL Error: %s", pData->m_pName, e.what());
|
||||
}
|
||||
#endif
|
||||
catch(std::runtime_error &e)
|
||||
{
|
||||
dbg_msg("sql", "%s SQLite Error: %s", pData->m_pName, e.what());
|
||||
|
|
|
@ -1,17 +1,145 @@
|
|||
#include "mysql.h"
|
||||
#include "connection.h"
|
||||
|
||||
#if defined(CONF_SQL)
|
||||
#include <mysql.h>
|
||||
|
||||
#include <base/tl/threading.h>
|
||||
#include <engine/console.h>
|
||||
#if defined(CONF_SQL)
|
||||
#include <cppconn/driver.h>
|
||||
#include <cppconn/exception.h>
|
||||
#include <cppconn/prepared_statement.h>
|
||||
#include <cppconn/statement.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
CLock CMysqlConnection::m_SqlDriverLock;
|
||||
enum
|
||||
{
|
||||
MYSQLSTATE_UNINITIALIZED,
|
||||
MYSQLSTATE_INITIALIZED,
|
||||
MYSQLSTATE_SHUTTINGDOWN,
|
||||
};
|
||||
|
||||
std::atomic_int g_MysqlState = {MYSQLSTATE_UNINITIALIZED};
|
||||
std::atomic_int g_MysqlNumConnections;
|
||||
|
||||
int MysqlInit()
|
||||
{
|
||||
dbg_assert(mysql_thread_safe(), "MySQL library without thread safety");
|
||||
dbg_assert(g_MysqlState == MYSQLSTATE_UNINITIALIZED, "double MySQL initialization");
|
||||
if(mysql_library_init(0, nullptr, nullptr))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
int Uninitialized = MYSQLSTATE_UNINITIALIZED;
|
||||
bool Swapped = g_MysqlState.compare_exchange_strong(Uninitialized, MYSQLSTATE_INITIALIZED);
|
||||
(void)Swapped;
|
||||
dbg_assert(Swapped, "MySQL double initialization");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MysqlUninit()
|
||||
{
|
||||
int Initialized = MYSQLSTATE_INITIALIZED;
|
||||
bool Swapped = g_MysqlState.compare_exchange_strong(Initialized, MYSQLSTATE_SHUTTINGDOWN);
|
||||
(void)Swapped;
|
||||
dbg_assert(Swapped, "double MySQL free or free without initialization");
|
||||
int Counter = g_MysqlNumConnections;
|
||||
if(Counter != 0)
|
||||
{
|
||||
dbg_msg("mysql", "can't deinitialize, connections remaining: %d", Counter);
|
||||
return;
|
||||
}
|
||||
mysql_library_end();
|
||||
}
|
||||
|
||||
class CMysqlConnection : public IDbConnection
|
||||
{
|
||||
public:
|
||||
CMysqlConnection(
|
||||
const char *pDatabase,
|
||||
const char *pPrefix,
|
||||
const char *pUser,
|
||||
const char *pPass,
|
||||
const char *pIp,
|
||||
int Port,
|
||||
bool Setup);
|
||||
virtual ~CMysqlConnection();
|
||||
virtual void Print(IConsole *pConsole, const char *Mode);
|
||||
|
||||
virtual CMysqlConnection *Copy();
|
||||
|
||||
virtual const char *BinaryCollate() const { return "utf8mb4_bin"; }
|
||||
virtual void ToUnixTimestamp(const char *pTimestamp, char *aBuf, unsigned int BufferSize);
|
||||
virtual const char *InsertTimestampAsUtc() const { return "?"; }
|
||||
virtual const char *CollateNocase() const { return "CONVERT(? USING utf8mb4) COLLATE utf8mb4_general_ci"; }
|
||||
virtual const char *InsertIgnore() const { return "INSERT IGNORE"; };
|
||||
virtual const char *Random() const { return "RAND()"; };
|
||||
virtual const char *MedianMapTime(char *pBuffer, int BufferSize) const;
|
||||
|
||||
virtual Status Connect();
|
||||
virtual void Disconnect();
|
||||
|
||||
virtual void PrepareStatement(const char *pStmt);
|
||||
|
||||
virtual void BindString(int Idx, const char *pString);
|
||||
virtual void BindBlob(int Idx, unsigned char *pBlob, int Size);
|
||||
virtual void BindInt(int Idx, int Value);
|
||||
virtual void BindFloat(int Idx, float Value);
|
||||
|
||||
virtual void Print() {}
|
||||
virtual bool Step();
|
||||
virtual int ExecuteUpdate();
|
||||
|
||||
virtual bool IsNull(int Col);
|
||||
virtual float GetFloat(int Col);
|
||||
virtual int GetInt(int Col);
|
||||
virtual void GetString(int Col, char *pBuffer, int BufferSize);
|
||||
virtual int GetBlob(int Col, unsigned char *pBuffer, int BufferSize);
|
||||
|
||||
virtual void AddPoints(const char *pPlayer, int Points);
|
||||
|
||||
private:
|
||||
class CStmtDeleter
|
||||
{
|
||||
public:
|
||||
void operator()(MYSQL_STMT *pStmt) const;
|
||||
};
|
||||
|
||||
char m_aErrorDetail[128];
|
||||
void StoreErrorMysql(const char *pContext);
|
||||
void StoreErrorStmt(const char *pContext);
|
||||
bool ConnectImpl();
|
||||
bool PrepareAndExecuteStatement(const char *pStmt);
|
||||
//static void DeleteResult(MYSQL_RES *pResult);
|
||||
|
||||
union UParameterExtra
|
||||
{
|
||||
int i;
|
||||
unsigned long ul;
|
||||
float f;
|
||||
};
|
||||
|
||||
bool m_NewQuery = false;
|
||||
bool m_HaveConnection = false;
|
||||
MYSQL m_Mysql;
|
||||
std::unique_ptr<MYSQL_STMT, CStmtDeleter> m_pStmt = nullptr;
|
||||
std::vector<MYSQL_BIND> m_aStmtParameters;
|
||||
std::vector<UParameterExtra> m_aStmtParameterExtras;
|
||||
|
||||
// copy of config vars
|
||||
char m_aDatabase[64];
|
||||
char m_aUser[64];
|
||||
char m_aPass[64];
|
||||
char m_aIp[64];
|
||||
int m_Port;
|
||||
bool m_Setup;
|
||||
|
||||
std::atomic_bool m_InUse;
|
||||
};
|
||||
|
||||
void CMysqlConnection::CStmtDeleter::operator()(MYSQL_STMT *pStmt) const
|
||||
{
|
||||
mysql_stmt_close(pStmt);
|
||||
}
|
||||
|
||||
CMysqlConnection::CMysqlConnection(
|
||||
const char *pDatabase,
|
||||
|
@ -22,24 +150,52 @@ CMysqlConnection::CMysqlConnection(
|
|||
int Port,
|
||||
bool Setup) :
|
||||
IDbConnection(pPrefix),
|
||||
#if defined(CONF_SQL)
|
||||
m_NewQuery(false),
|
||||
#endif
|
||||
m_Port(Port),
|
||||
m_Setup(Setup),
|
||||
m_InUse(false)
|
||||
{
|
||||
g_MysqlNumConnections += 1;
|
||||
dbg_assert(g_MysqlState == MYSQLSTATE_INITIALIZED, "MySQL library not in initialized state");
|
||||
|
||||
mem_zero(m_aErrorDetail, sizeof(m_aErrorDetail));
|
||||
mem_zero(&m_Mysql, sizeof(m_Mysql));
|
||||
mysql_init(&m_Mysql);
|
||||
|
||||
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));
|
||||
#ifndef CONF_SQL
|
||||
dbg_msg("sql", "Adding MySQL server failed due to MySQL support not enabled during compile time");
|
||||
#endif
|
||||
}
|
||||
|
||||
CMysqlConnection::~CMysqlConnection()
|
||||
{
|
||||
mysql_close(&m_Mysql);
|
||||
g_MysqlNumConnections -= 1;
|
||||
}
|
||||
|
||||
void CMysqlConnection::StoreErrorMysql(const char *pContext)
|
||||
{
|
||||
str_format(m_aErrorDetail, sizeof(m_aErrorDetail), "(%s:mysql:%d): %s", pContext, mysql_errno(&m_Mysql), mysql_error(&m_Mysql));
|
||||
}
|
||||
|
||||
void CMysqlConnection::StoreErrorStmt(const char *pContext)
|
||||
{
|
||||
str_format(m_aErrorDetail, sizeof(m_aErrorDetail), "(%s:stmt:%d): %s", pContext, mysql_stmt_errno(m_pStmt.get()), mysql_stmt_error(m_pStmt.get()));
|
||||
}
|
||||
|
||||
bool CMysqlConnection::PrepareAndExecuteStatement(const char *pStmt)
|
||||
{
|
||||
if(mysql_stmt_prepare(m_pStmt.get(), pStmt, str_length(pStmt)))
|
||||
{
|
||||
StoreErrorStmt("prepare");
|
||||
return true;
|
||||
}
|
||||
if(mysql_stmt_execute(m_pStmt.get()))
|
||||
{
|
||||
StoreErrorStmt("execute");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CMysqlConnection::Print(IConsole *pConsole, const char *Mode)
|
||||
|
@ -63,119 +219,109 @@ void CMysqlConnection::ToUnixTimestamp(const char *pTimestamp, char *aBuf, unsig
|
|||
|
||||
IDbConnection::Status CMysqlConnection::Connect()
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
if(m_InUse.exchange(true))
|
||||
return Status::IN_USE;
|
||||
|
||||
m_NewQuery = true;
|
||||
if(m_pConnection != nullptr)
|
||||
if(ConnectImpl())
|
||||
{
|
||||
try
|
||||
{
|
||||
// Connect to specific database
|
||||
m_pConnection->setSchema(m_aDatabase);
|
||||
dbg_msg("mysql", "connect error %s", m_aErrorDetail);
|
||||
m_InUse.store(false);
|
||||
return Status::FAILURE;
|
||||
}
|
||||
return Status::SUCCESS;
|
||||
}
|
||||
catch(sql::SQLException &e)
|
||||
|
||||
bool CMysqlConnection::ConnectImpl()
|
||||
{
|
||||
dbg_msg("sql", "MySQL Error: %s", e.what());
|
||||
if(m_HaveConnection)
|
||||
{
|
||||
if(m_pStmt && mysql_stmt_free_result(m_pStmt.get()))
|
||||
{
|
||||
StoreErrorStmt("free_result");
|
||||
dbg_msg("mysql", "can't free last result %s", m_aErrorDetail);
|
||||
}
|
||||
catch(const std::exception &ex)
|
||||
if(!mysql_select_db(&m_Mysql, m_aDatabase))
|
||||
{
|
||||
dbg_msg("sql", "MySQL Error: %s", ex.what());
|
||||
// Success.
|
||||
return false;
|
||||
}
|
||||
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");
|
||||
StoreErrorMysql("select_db");
|
||||
dbg_msg("mysql", "ping error, trying to reconnect %s", m_aErrorDetail);
|
||||
mysql_close(&m_Mysql);
|
||||
mem_zero(&m_Mysql, sizeof(m_Mysql));
|
||||
mysql_init(&m_Mysql);
|
||||
}
|
||||
|
||||
dbg_msg("sql", "FAILURE: SQL connection failed, trying to reconnect");
|
||||
}
|
||||
m_pStmt = nullptr;
|
||||
unsigned int OptConnectTimeout = 60;
|
||||
unsigned int OptReadTimeout = 60;
|
||||
unsigned int OptWriteTimeout = 120;
|
||||
my_bool OptReconnect = true;
|
||||
mysql_options(&m_Mysql, MYSQL_OPT_CONNECT_TIMEOUT, &OptConnectTimeout);
|
||||
mysql_options(&m_Mysql, MYSQL_OPT_READ_TIMEOUT, &OptReadTimeout);
|
||||
mysql_options(&m_Mysql, MYSQL_OPT_WRITE_TIMEOUT, &OptWriteTimeout);
|
||||
mysql_options(&m_Mysql, MYSQL_OPT_RECONNECT, &OptReconnect);
|
||||
mysql_options(&m_Mysql, MYSQL_SET_CHARSET_NAME, "utf8mb4");
|
||||
|
||||
try
|
||||
if(!mysql_real_connect(&m_Mysql, m_aIp, m_aUser, m_aPass, nullptr, m_Port, nullptr, CLIENT_IGNORE_SIGPIPE))
|
||||
{
|
||||
m_pConnection.reset();
|
||||
m_pPreparedStmt.reset();
|
||||
m_pResults.reset();
|
||||
|
||||
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"] = 60;
|
||||
connection_properties["OPT_READ_TIMEOUT"] = 60;
|
||||
connection_properties["OPT_WRITE_TIMEOUT"] = 120;
|
||||
connection_properties["OPT_RECONNECT"] = true;
|
||||
connection_properties["OPT_CHARSET_NAME"] = sql::SQLString("utf8mb4");
|
||||
connection_properties["OPT_SET_CHARSET_NAME"] = sql::SQLString("utf8mb4");
|
||||
|
||||
// Create connection
|
||||
{
|
||||
CScopeLock GlobalLockScope(&m_SqlDriverLock);
|
||||
sql::Driver *pDriver = get_driver_instance();
|
||||
m_pConnection.reset(pDriver->connect(connection_properties));
|
||||
StoreErrorMysql("real_connect");
|
||||
return true;
|
||||
}
|
||||
m_HaveConnection = true;
|
||||
|
||||
// Create Statement
|
||||
m_pStmt = std::unique_ptr<sql::Statement>(m_pConnection->createStatement());
|
||||
m_pStmt = std::unique_ptr<MYSQL_STMT, CStmtDeleter>(mysql_stmt_init(&m_Mysql));
|
||||
|
||||
// Apparently OPT_CHARSET_NAME and OPT_SET_CHARSET_NAME are not enough
|
||||
m_pStmt->execute("SET CHARACTER SET utf8mb4;");
|
||||
// Apparently MYSQL_SET_CHARSET_NAME is not enough
|
||||
if(PrepareAndExecuteStatement("SET CHARACTER SET utf8mb4"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if(m_Setup)
|
||||
{
|
||||
char aBuf[1024];
|
||||
char aCreateDatabase[1024];
|
||||
// create database
|
||||
str_format(aBuf, sizeof(aBuf), "CREATE DATABASE IF NOT EXISTS %s CHARACTER SET utf8mb4", m_aDatabase);
|
||||
m_pStmt->execute(aBuf);
|
||||
str_format(aCreateDatabase, sizeof(aCreateDatabase), "CREATE DATABASE IF NOT EXISTS %s CHARACTER SET utf8mb4", m_aDatabase);
|
||||
if(PrepareAndExecuteStatement(aCreateDatabase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Connect to specific database
|
||||
m_pConnection->setSchema(m_aDatabase);
|
||||
FormatCreateRace(aBuf, sizeof(aBuf));
|
||||
m_pStmt->execute(aBuf);
|
||||
FormatCreateTeamrace(aBuf, sizeof(aBuf), "VARBINARY(16)");
|
||||
m_pStmt->execute(aBuf);
|
||||
FormatCreateMaps(aBuf, sizeof(aBuf));
|
||||
m_pStmt->execute(aBuf);
|
||||
FormatCreateSaves(aBuf, sizeof(aBuf));
|
||||
m_pStmt->execute(aBuf);
|
||||
FormatCreatePoints(aBuf, sizeof(aBuf));
|
||||
m_pStmt->execute(aBuf);
|
||||
if(mysql_select_db(&m_Mysql, m_aDatabase))
|
||||
{
|
||||
StoreErrorMysql("select_db");
|
||||
return true;
|
||||
}
|
||||
|
||||
if(m_Setup)
|
||||
{
|
||||
char aCreateRace[1024];
|
||||
char aCreateTeamrace[1024];
|
||||
char aCreateMaps[1024];
|
||||
char aCreateSaves[1024];
|
||||
char aCreatePoints[1024];
|
||||
FormatCreateRace(aCreateRace, sizeof(aCreateRace));
|
||||
FormatCreateTeamrace(aCreateTeamrace, sizeof(aCreateTeamrace), "VARBINARY(16)");
|
||||
FormatCreateMaps(aCreateMaps, sizeof(aCreateMaps));
|
||||
FormatCreateSaves(aCreateSaves, sizeof(aCreateSaves));
|
||||
FormatCreatePoints(aCreatePoints, sizeof(aCreatePoints));
|
||||
|
||||
if(PrepareAndExecuteStatement(aCreateRace) ||
|
||||
PrepareAndExecuteStatement(aCreateTeamrace) ||
|
||||
PrepareAndExecuteStatement(aCreateMaps) ||
|
||||
PrepareAndExecuteStatement(aCreateSaves) ||
|
||||
PrepareAndExecuteStatement(aCreatePoints))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
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");
|
||||
}
|
||||
m_InUse.store(false);
|
||||
|
||||
#endif
|
||||
dbg_msg("sql", "FAILURE: sql connection failed");
|
||||
return Status::FAILURE;
|
||||
dbg_msg("mysql", "connection established");
|
||||
return false;
|
||||
}
|
||||
|
||||
void CMysqlConnection::Disconnect()
|
||||
|
@ -185,118 +331,303 @@ void CMysqlConnection::Disconnect()
|
|||
|
||||
void CMysqlConnection::PrepareStatement(const char *pStmt)
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
m_pPreparedStmt.reset(m_pConnection->prepareStatement(pStmt));
|
||||
if(mysql_stmt_prepare(m_pStmt.get(), pStmt, str_length(pStmt)))
|
||||
{
|
||||
StoreErrorStmt("prepare");
|
||||
dbg_msg("mysql", "error preparing statement %s", m_aErrorDetail);
|
||||
throw std::runtime_error(m_aErrorDetail);
|
||||
// TODO: return true;
|
||||
}
|
||||
m_NewQuery = true;
|
||||
#endif
|
||||
unsigned NumParameters = mysql_stmt_param_count(m_pStmt.get());
|
||||
m_aStmtParameters.resize(NumParameters);
|
||||
m_aStmtParameterExtras.resize(NumParameters);
|
||||
mem_zero(&m_aStmtParameters[0], sizeof(m_aStmtParameters[0]) * m_aStmtParameters.size());
|
||||
mem_zero(&m_aStmtParameterExtras[0], sizeof(m_aStmtParameterExtras[0]) * m_aStmtParameterExtras.size());
|
||||
// TODO: return false;
|
||||
}
|
||||
|
||||
void CMysqlConnection::BindString(int Idx, const char *pString)
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
m_pPreparedStmt->setString(Idx, pString);
|
||||
m_NewQuery = true;
|
||||
#endif
|
||||
Idx -= 1;
|
||||
dbg_assert(0 <= Idx && Idx < (int)m_aStmtParameters.size(), "index out of bounds");
|
||||
|
||||
int Length = str_length(pString);
|
||||
m_aStmtParameterExtras[Idx].ul = Length;
|
||||
MYSQL_BIND *pParam = &m_aStmtParameters[Idx];
|
||||
pParam->buffer_type = MYSQL_TYPE_STRING;
|
||||
pParam->buffer = (void *)pString;
|
||||
pParam->buffer_length = Length + 1;
|
||||
pParam->length = &m_aStmtParameterExtras[Idx].ul;
|
||||
pParam->is_null = nullptr;
|
||||
pParam->is_unsigned = false;
|
||||
pParam->error = nullptr;
|
||||
}
|
||||
|
||||
void CMysqlConnection::BindBlob(int Idx, unsigned char *pBlob, int Size)
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
// copy blob into string
|
||||
auto Blob = std::string(pBlob, pBlob + Size);
|
||||
m_pPreparedStmt->setString(Idx, Blob);
|
||||
m_NewQuery = true;
|
||||
#endif
|
||||
Idx -= 1;
|
||||
dbg_assert(0 <= Idx && Idx < (int)m_aStmtParameters.size(), "index out of bounds");
|
||||
|
||||
m_aStmtParameterExtras[Idx].ul = Size;
|
||||
MYSQL_BIND *pParam = &m_aStmtParameters[Idx];
|
||||
pParam->buffer_type = MYSQL_TYPE_BLOB;
|
||||
pParam->buffer = pBlob;
|
||||
pParam->buffer_length = Size;
|
||||
pParam->length = &m_aStmtParameterExtras[Idx].ul;
|
||||
pParam->is_null = nullptr;
|
||||
pParam->is_unsigned = false;
|
||||
pParam->error = nullptr;
|
||||
}
|
||||
|
||||
void CMysqlConnection::BindInt(int Idx, int Value)
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
m_pPreparedStmt->setInt(Idx, Value);
|
||||
m_NewQuery = true;
|
||||
#endif
|
||||
Idx -= 1;
|
||||
dbg_assert(0 <= Idx && Idx < (int)m_aStmtParameters.size(), "index out of bounds");
|
||||
|
||||
m_aStmtParameterExtras[Idx].i = Value;
|
||||
MYSQL_BIND *pParam = &m_aStmtParameters[Idx];
|
||||
pParam->buffer_type = MYSQL_TYPE_LONG;
|
||||
pParam->buffer = &m_aStmtParameterExtras[Idx].i;
|
||||
pParam->buffer_length = sizeof(m_aStmtParameterExtras[Idx].i);
|
||||
pParam->length = nullptr;
|
||||
pParam->is_null = nullptr;
|
||||
pParam->is_unsigned = false;
|
||||
pParam->error = nullptr;
|
||||
}
|
||||
|
||||
void CMysqlConnection::BindFloat(int Idx, float Value)
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
m_pPreparedStmt->setDouble(Idx, (double)Value);
|
||||
m_NewQuery = true;
|
||||
#endif
|
||||
Idx -= 1;
|
||||
dbg_assert(0 <= Idx && Idx < (int)m_aStmtParameters.size(), "index out of bounds");
|
||||
|
||||
m_aStmtParameterExtras[Idx].f = Value;
|
||||
MYSQL_BIND *pParam = &m_aStmtParameters[Idx];
|
||||
pParam->buffer_type = MYSQL_TYPE_FLOAT;
|
||||
pParam->buffer = &m_aStmtParameterExtras[Idx].f;
|
||||
pParam->buffer_length = sizeof(m_aStmtParameterExtras[Idx].i);
|
||||
pParam->length = nullptr;
|
||||
pParam->is_null = nullptr;
|
||||
pParam->is_unsigned = false;
|
||||
pParam->error = nullptr;
|
||||
}
|
||||
|
||||
bool CMysqlConnection::Step()
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
if(m_NewQuery)
|
||||
{
|
||||
m_NewQuery = false;
|
||||
m_pResults.reset(m_pPreparedStmt->executeQuery());
|
||||
if(mysql_stmt_bind_param(m_pStmt.get(), &m_aStmtParameters[0]))
|
||||
{
|
||||
StoreErrorStmt("bind_param");
|
||||
dbg_msg("mysql", "error binding params %s", m_aErrorDetail);
|
||||
throw std::runtime_error(m_aErrorDetail);
|
||||
// TODO: error handling
|
||||
}
|
||||
return m_pResults->next();
|
||||
#else
|
||||
if(mysql_stmt_execute(m_pStmt.get()))
|
||||
{
|
||||
StoreErrorStmt("execute");
|
||||
dbg_msg("mysql", "error executing query %s", m_aErrorDetail);
|
||||
throw std::runtime_error(m_aErrorDetail);
|
||||
// TODO: error handling
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
int Result = mysql_stmt_fetch(m_pStmt.get());
|
||||
if(Result == 1)
|
||||
{
|
||||
StoreErrorStmt("fetch");
|
||||
dbg_msg("mysql", "error fetching row %s", m_aErrorDetail);
|
||||
throw std::runtime_error(m_aErrorDetail);
|
||||
// TODO: error handling
|
||||
return false;
|
||||
}
|
||||
if(Result == MYSQL_NO_DATA)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// `Result` is now either `MYSQL_DATA_TRUNCATED` (which we ignore, we
|
||||
// fetch our columns in a different way) or `0` aka success.
|
||||
return true;
|
||||
}
|
||||
|
||||
int CMysqlConnection::ExecuteUpdate()
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
if(m_NewQuery)
|
||||
{
|
||||
m_NewQuery = false;
|
||||
return m_pPreparedStmt->executeUpdate();
|
||||
if(mysql_stmt_bind_param(m_pStmt.get(), &m_aStmtParameters[0]))
|
||||
{
|
||||
StoreErrorStmt("bind_param");
|
||||
dbg_msg("mysql", "error binding params %s", m_aErrorDetail);
|
||||
throw std::runtime_error(m_aErrorDetail);
|
||||
// TODO: error handling
|
||||
return -1;
|
||||
}
|
||||
if(mysql_stmt_execute(m_pStmt.get()))
|
||||
{
|
||||
StoreErrorStmt("execute");
|
||||
dbg_msg("mysql", "error executing update %s", m_aErrorDetail);
|
||||
throw std::runtime_error(m_aErrorDetail);
|
||||
// TODO: error handling
|
||||
return -1;
|
||||
}
|
||||
return mysql_stmt_affected_rows(m_pStmt.get());
|
||||
}
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool CMysqlConnection::IsNull(int Col) const
|
||||
bool CMysqlConnection::IsNull(int Col)
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
return m_pResults->isNull(Col);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
Col -= 1;
|
||||
|
||||
MYSQL_BIND Bind;
|
||||
my_bool IsNull;
|
||||
mem_zero(&Bind, sizeof(Bind));
|
||||
Bind.buffer_type = MYSQL_TYPE_NULL;
|
||||
Bind.buffer = nullptr;
|
||||
Bind.buffer_length = 0;
|
||||
Bind.length = nullptr;
|
||||
Bind.is_null = &IsNull;
|
||||
Bind.is_unsigned = false;
|
||||
Bind.error = nullptr;
|
||||
if(mysql_stmt_fetch_column(m_pStmt.get(), &Bind, Col, 0))
|
||||
{
|
||||
StoreErrorStmt("fetch_column:null");
|
||||
dbg_msg("mysql", "error fetching column %s", m_aErrorDetail);
|
||||
dbg_assert(0, "error in IsNull");
|
||||
}
|
||||
return IsNull;
|
||||
}
|
||||
|
||||
float CMysqlConnection::GetFloat(int Col) const
|
||||
float CMysqlConnection::GetFloat(int Col)
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
return (float)m_pResults->getDouble(Col);
|
||||
#else
|
||||
return 0.0;
|
||||
#endif
|
||||
Col -= 1;
|
||||
|
||||
MYSQL_BIND Bind;
|
||||
float Value;
|
||||
my_bool IsNull;
|
||||
mem_zero(&Bind, sizeof(Bind));
|
||||
Bind.buffer_type = MYSQL_TYPE_FLOAT;
|
||||
Bind.buffer = &Value;
|
||||
Bind.buffer_length = sizeof(Value);
|
||||
Bind.length = nullptr;
|
||||
Bind.is_null = &IsNull;
|
||||
Bind.is_unsigned = false;
|
||||
Bind.error = nullptr;
|
||||
if(mysql_stmt_fetch_column(m_pStmt.get(), &Bind, Col, 0))
|
||||
{
|
||||
StoreErrorStmt("fetch_column:float");
|
||||
dbg_msg("mysql", "error fetching column %s", m_aErrorDetail);
|
||||
dbg_assert(0, "error in GetFloat");
|
||||
}
|
||||
if(IsNull)
|
||||
{
|
||||
dbg_assert(0, "error getting float: NULL");
|
||||
}
|
||||
return Value;
|
||||
}
|
||||
|
||||
int CMysqlConnection::GetInt(int Col) const
|
||||
int CMysqlConnection::GetInt(int Col)
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
return m_pResults->getInt(Col);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
Col -= 1;
|
||||
|
||||
MYSQL_BIND Bind;
|
||||
int Value;
|
||||
my_bool IsNull;
|
||||
mem_zero(&Bind, sizeof(Bind));
|
||||
Bind.buffer_type = MYSQL_TYPE_LONG;
|
||||
Bind.buffer = &Value;
|
||||
Bind.buffer_length = sizeof(Value);
|
||||
Bind.length = nullptr;
|
||||
Bind.is_null = &IsNull;
|
||||
Bind.is_unsigned = false;
|
||||
Bind.error = nullptr;
|
||||
if(mysql_stmt_fetch_column(m_pStmt.get(), &Bind, Col, 0))
|
||||
{
|
||||
StoreErrorStmt("fetch_column:int");
|
||||
dbg_msg("mysql", "error fetching column %s", m_aErrorDetail);
|
||||
dbg_assert(0, "error in GetInt");
|
||||
}
|
||||
if(IsNull)
|
||||
{
|
||||
dbg_assert(0, "error getting int: NULL");
|
||||
}
|
||||
return Value;
|
||||
}
|
||||
|
||||
void CMysqlConnection::GetString(int Col, char *pBuffer, int BufferSize) const
|
||||
void CMysqlConnection::GetString(int Col, char *pBuffer, int BufferSize)
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
auto String = m_pResults->getString(Col);
|
||||
str_copy(pBuffer, String.c_str(), BufferSize);
|
||||
#endif
|
||||
Col -= 1;
|
||||
|
||||
for(int i = 0; i < BufferSize; i++)
|
||||
{
|
||||
pBuffer[i] = 'a';
|
||||
}
|
||||
|
||||
int CMysqlConnection::GetBlob(int Col, unsigned char *pBuffer, int BufferSize) const
|
||||
MYSQL_BIND Bind;
|
||||
unsigned long Length;
|
||||
my_bool IsNull;
|
||||
my_bool Error;
|
||||
mem_zero(&Bind, sizeof(Bind));
|
||||
Bind.buffer_type = MYSQL_TYPE_STRING;
|
||||
Bind.buffer = pBuffer;
|
||||
Bind.buffer_length = BufferSize;
|
||||
Bind.length = &Length;
|
||||
Bind.is_null = &IsNull;
|
||||
Bind.is_unsigned = false;
|
||||
Bind.error = &Error;
|
||||
if(mysql_stmt_fetch_column(m_pStmt.get(), &Bind, Col, 0))
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
auto *Blob = m_pResults->getBlob(Col);
|
||||
Blob->read((char *)pBuffer, BufferSize);
|
||||
int NumRead = Blob->gcount();
|
||||
delete Blob;
|
||||
return NumRead;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
StoreErrorStmt("fetch_column:string");
|
||||
dbg_msg("mysql", "error fetching column %s", m_aErrorDetail);
|
||||
dbg_assert(0, "error in GetString");
|
||||
}
|
||||
if(IsNull)
|
||||
{
|
||||
dbg_assert(0, "error getting string: NULL");
|
||||
}
|
||||
if(Error)
|
||||
{
|
||||
dbg_assert(0, "error getting string: truncation occured");
|
||||
}
|
||||
}
|
||||
|
||||
int CMysqlConnection::GetBlob(int Col, unsigned char *pBuffer, int BufferSize)
|
||||
{
|
||||
Col -= 1;
|
||||
|
||||
MYSQL_BIND Bind;
|
||||
unsigned long Length;
|
||||
my_bool IsNull;
|
||||
my_bool Error;
|
||||
mem_zero(&Bind, sizeof(Bind));
|
||||
Bind.buffer_type = MYSQL_TYPE_BLOB;
|
||||
Bind.buffer = pBuffer;
|
||||
Bind.buffer_length = BufferSize;
|
||||
Bind.length = &Length;
|
||||
Bind.is_null = &IsNull;
|
||||
Bind.is_unsigned = false;
|
||||
Bind.error = &Error;
|
||||
if(mysql_stmt_fetch_column(m_pStmt.get(), &Bind, Col, 0))
|
||||
{
|
||||
StoreErrorStmt("fetch_column:blob");
|
||||
dbg_msg("mysql", "error fetching column %s", m_aErrorDetail);
|
||||
dbg_assert(0, "error in GetBlob");
|
||||
}
|
||||
if(IsNull)
|
||||
{
|
||||
dbg_assert(0, "error getting blob: NULL");
|
||||
}
|
||||
if(Error)
|
||||
{
|
||||
dbg_assert(0, "error getting blob: truncation occured");
|
||||
}
|
||||
return Length;
|
||||
}
|
||||
|
||||
const char *CMysqlConnection::MedianMapTime(char *pBuffer, int BufferSize) const
|
||||
|
@ -317,7 +648,7 @@ void CMysqlConnection::AddPoints(const char *pPlayer, int Points)
|
|||
str_format(aBuf, sizeof(aBuf),
|
||||
"INSERT INTO %s_points(Name, Points) "
|
||||
"VALUES (?, ?) "
|
||||
"ON DUPLICATE KEY UPDATE Points=Points+?;",
|
||||
"ON DUPLICATE KEY UPDATE Points=Points+?",
|
||||
GetPrefix());
|
||||
PrepareStatement(aBuf);
|
||||
BindString(1, pPlayer);
|
||||
|
@ -325,3 +656,35 @@ void CMysqlConnection::AddPoints(const char *pPlayer, int Points)
|
|||
BindInt(3, Points);
|
||||
Step();
|
||||
}
|
||||
|
||||
IDbConnection *CreateMysqlConnection(
|
||||
const char *pDatabase,
|
||||
const char *pPrefix,
|
||||
const char *pUser,
|
||||
const char *pPass,
|
||||
const char *pIp,
|
||||
int Port,
|
||||
bool Setup)
|
||||
{
|
||||
return new CMysqlConnection(pDatabase, pPrefix, pUser, pPass, pIp, Port, Setup);
|
||||
}
|
||||
#else
|
||||
int MysqlInit()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
void MysqlUninit()
|
||||
{
|
||||
}
|
||||
IDbConnection *CreateMysqlConnection(
|
||||
const char *pDatabase,
|
||||
const char *pPrefix,
|
||||
const char *pUser,
|
||||
const char *pPass,
|
||||
const char *pIp,
|
||||
int Port,
|
||||
bool Setup)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
#ifndef ENGINE_SERVER_DATABASES_MYSQL_H
|
||||
#define ENGINE_SERVER_DATABASES_MYSQL_H
|
||||
|
||||
#include <atomic>
|
||||
#include <engine/server/databases/connection.h>
|
||||
#include <memory>
|
||||
|
||||
class CLock;
|
||||
namespace sql {
|
||||
class Connection;
|
||||
class PreparedStatement;
|
||||
class ResultSet;
|
||||
class Statement;
|
||||
} /* namespace sql */
|
||||
|
||||
class CMysqlConnection : public IDbConnection
|
||||
{
|
||||
public:
|
||||
CMysqlConnection(
|
||||
const char *pDatabase,
|
||||
const char *pPrefix,
|
||||
const char *pUser,
|
||||
const char *pPass,
|
||||
const char *pIp,
|
||||
int Port,
|
||||
bool Setup);
|
||||
virtual ~CMysqlConnection();
|
||||
virtual void Print(IConsole *pConsole, const char *Mode);
|
||||
|
||||
virtual CMysqlConnection *Copy();
|
||||
|
||||
virtual const char *BinaryCollate() const { return "utf8mb4_bin"; }
|
||||
virtual void ToUnixTimestamp(const char *pTimestamp, char *aBuf, unsigned int BufferSize);
|
||||
virtual const char *InsertTimestampAsUtc() const { return "?"; }
|
||||
virtual const char *CollateNocase() const { return "CONVERT(? USING utf8mb4) COLLATE utf8mb4_general_ci"; }
|
||||
virtual const char *InsertIgnore() const { return "INSERT IGNORE"; };
|
||||
virtual const char *Random() const { return "RAND()"; };
|
||||
virtual const char *MedianMapTime(char *pBuffer, int BufferSize) const;
|
||||
|
||||
virtual Status Connect();
|
||||
virtual void Disconnect();
|
||||
|
||||
virtual void PrepareStatement(const char *pStmt);
|
||||
|
||||
virtual void BindString(int Idx, const char *pString);
|
||||
virtual void BindBlob(int Idx, unsigned char *pBlob, int Size);
|
||||
virtual void BindInt(int Idx, int Value);
|
||||
virtual void BindFloat(int Idx, float Value);
|
||||
|
||||
virtual void Print() {}
|
||||
virtual bool Step();
|
||||
virtual int ExecuteUpdate();
|
||||
|
||||
virtual bool IsNull(int Col) const;
|
||||
virtual float GetFloat(int Col) const;
|
||||
virtual int GetInt(int Col) const;
|
||||
virtual void GetString(int Col, char *pBuffer, int BufferSize) const;
|
||||
virtual int GetBlob(int Col, unsigned char *pBuffer, int BufferSize) const;
|
||||
|
||||
virtual void AddPoints(const char *pPlayer, int Points);
|
||||
|
||||
private:
|
||||
#if defined(CONF_SQL)
|
||||
std::unique_ptr<sql::Connection> m_pConnection;
|
||||
std::unique_ptr<sql::PreparedStatement> m_pPreparedStmt;
|
||||
std::unique_ptr<sql::Statement> m_pStmt;
|
||||
std::unique_ptr<sql::ResultSet> m_pResults;
|
||||
bool m_NewQuery;
|
||||
#endif
|
||||
|
||||
// copy of config vars
|
||||
char m_aDatabase[64];
|
||||
char m_aUser[64];
|
||||
char m_aPass[64];
|
||||
char m_aIp[64];
|
||||
int m_Port;
|
||||
bool m_Setup;
|
||||
|
||||
std::atomic_bool m_InUse;
|
||||
static CLock m_SqlDriverLock;
|
||||
};
|
||||
|
||||
#endif // ENGINE_SERVER_DATABASES_MYSQL_H
|
|
@ -1,11 +1,69 @@
|
|||
#include "sqlite.h"
|
||||
#include "connection.h"
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <base/math.h>
|
||||
#include <engine/console.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <atomic>
|
||||
#include <stdexcept>
|
||||
|
||||
class CSqliteConnection : public IDbConnection
|
||||
{
|
||||
public:
|
||||
CSqliteConnection(const char *pFilename, bool Setup);
|
||||
virtual ~CSqliteConnection();
|
||||
virtual void Print(IConsole *pConsole, const char *Mode);
|
||||
|
||||
virtual CSqliteConnection *Copy();
|
||||
|
||||
virtual const char *BinaryCollate() const { return "BINARY"; }
|
||||
virtual void ToUnixTimestamp(const char *pTimestamp, char *aBuf, unsigned int BufferSize);
|
||||
virtual const char *InsertTimestampAsUtc() const { return "DATETIME(?, 'utc')"; }
|
||||
virtual const char *CollateNocase() const { return "? COLLATE NOCASE"; }
|
||||
virtual const char *InsertIgnore() const { return "INSERT OR IGNORE"; };
|
||||
virtual const char *Random() const { return "RANDOM()"; };
|
||||
virtual const char *MedianMapTime(char *pBuffer, int BufferSize) const;
|
||||
|
||||
virtual Status Connect();
|
||||
virtual void Disconnect();
|
||||
|
||||
virtual void PrepareStatement(const char *pStmt);
|
||||
|
||||
virtual void BindString(int Idx, const char *pString);
|
||||
virtual void BindBlob(int Idx, unsigned char *pBlob, int Size);
|
||||
virtual void BindInt(int Idx, int Value);
|
||||
virtual void BindFloat(int Idx, float Value);
|
||||
|
||||
virtual void Print();
|
||||
virtual bool Step();
|
||||
virtual int ExecuteUpdate();
|
||||
|
||||
virtual bool IsNull(int Col);
|
||||
virtual float GetFloat(int Col);
|
||||
virtual int GetInt(int Col);
|
||||
virtual void GetString(int Col, char *pBuffer, int BufferSize);
|
||||
// passing a negative buffer size is undefined behavior
|
||||
virtual int GetBlob(int Col, unsigned char *pBuffer, int BufferSize);
|
||||
|
||||
virtual void AddPoints(const char *pPlayer, int Points);
|
||||
|
||||
private:
|
||||
// copy of config vars
|
||||
char m_aFilename[512];
|
||||
bool m_Setup;
|
||||
|
||||
sqlite3 *m_pDb;
|
||||
sqlite3_stmt *m_pStmt;
|
||||
bool m_Done; // no more rows available for Step
|
||||
// returns true, if the query succeeded
|
||||
bool Execute(const char *pQuery);
|
||||
|
||||
void ExceptionOnError(int Result);
|
||||
|
||||
std::atomic_bool m_InUse;
|
||||
};
|
||||
|
||||
CSqliteConnection::CSqliteConnection(const char *pFilename, bool Setup) :
|
||||
IDbConnection("record"),
|
||||
m_Setup(Setup),
|
||||
|
@ -178,27 +236,27 @@ int CSqliteConnection::ExecuteUpdate()
|
|||
return sqlite3_changes(m_pDb);
|
||||
}
|
||||
|
||||
bool CSqliteConnection::IsNull(int Col) const
|
||||
bool CSqliteConnection::IsNull(int Col)
|
||||
{
|
||||
return sqlite3_column_type(m_pStmt, Col - 1) == SQLITE_NULL;
|
||||
}
|
||||
|
||||
float CSqliteConnection::GetFloat(int Col) const
|
||||
float CSqliteConnection::GetFloat(int Col)
|
||||
{
|
||||
return (float)sqlite3_column_double(m_pStmt, Col - 1);
|
||||
}
|
||||
|
||||
int CSqliteConnection::GetInt(int Col) const
|
||||
int CSqliteConnection::GetInt(int Col)
|
||||
{
|
||||
return sqlite3_column_int(m_pStmt, Col - 1);
|
||||
}
|
||||
|
||||
void CSqliteConnection::GetString(int Col, char *pBuffer, int BufferSize) const
|
||||
void CSqliteConnection::GetString(int Col, char *pBuffer, int BufferSize)
|
||||
{
|
||||
str_copy(pBuffer, (const char *)sqlite3_column_text(m_pStmt, Col - 1), BufferSize);
|
||||
}
|
||||
|
||||
int CSqliteConnection::GetBlob(int Col, unsigned char *pBuffer, int BufferSize) const
|
||||
int CSqliteConnection::GetBlob(int Col, unsigned char *pBuffer, int BufferSize)
|
||||
{
|
||||
int Size = sqlite3_column_bytes(m_pStmt, Col - 1);
|
||||
Size = minimum(Size, BufferSize);
|
||||
|
@ -257,3 +315,8 @@ void CSqliteConnection::AddPoints(const char *pPlayer, int Points)
|
|||
BindInt(3, Points);
|
||||
Step();
|
||||
}
|
||||
|
||||
IDbConnection *CreateSqliteConnection(const char *pFilename, bool Setup)
|
||||
{
|
||||
return new CSqliteConnection(pFilename, Setup);
|
||||
}
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
#ifndef ENGINE_SERVER_DATABASES_SQLITE_H
|
||||
#define ENGINE_SERVER_DATABASES_SQLITE_H
|
||||
|
||||
#include "connection.h"
|
||||
#include <atomic>
|
||||
|
||||
struct sqlite3;
|
||||
struct sqlite3_stmt;
|
||||
|
||||
class CSqliteConnection : public IDbConnection
|
||||
{
|
||||
public:
|
||||
CSqliteConnection(const char *pFilename, bool Setup);
|
||||
virtual ~CSqliteConnection();
|
||||
virtual void Print(IConsole *pConsole, const char *Mode);
|
||||
|
||||
virtual CSqliteConnection *Copy();
|
||||
|
||||
virtual const char *BinaryCollate() const { return "BINARY"; }
|
||||
virtual void ToUnixTimestamp(const char *pTimestamp, char *aBuf, unsigned int BufferSize);
|
||||
virtual const char *InsertTimestampAsUtc() const { return "DATETIME(?, 'utc')"; }
|
||||
virtual const char *CollateNocase() const { return "? COLLATE NOCASE"; }
|
||||
virtual const char *InsertIgnore() const { return "INSERT OR IGNORE"; };
|
||||
virtual const char *Random() const { return "RANDOM()"; };
|
||||
virtual const char *MedianMapTime(char *pBuffer, int BufferSize) const;
|
||||
|
||||
virtual Status Connect();
|
||||
virtual void Disconnect();
|
||||
|
||||
virtual void PrepareStatement(const char *pStmt);
|
||||
|
||||
virtual void BindString(int Idx, const char *pString);
|
||||
virtual void BindBlob(int Idx, unsigned char *pBlob, int Size);
|
||||
virtual void BindInt(int Idx, int Value);
|
||||
virtual void BindFloat(int Idx, float Value);
|
||||
|
||||
virtual void Print();
|
||||
virtual bool Step();
|
||||
virtual int ExecuteUpdate();
|
||||
|
||||
virtual bool IsNull(int Col) const;
|
||||
virtual float GetFloat(int Col) const;
|
||||
virtual int GetInt(int Col) const;
|
||||
virtual void GetString(int Col, char *pBuffer, int BufferSize) const;
|
||||
// passing a negative buffer size is undefined behavior
|
||||
virtual int GetBlob(int Col, unsigned char *pBuffer, int BufferSize) const;
|
||||
|
||||
virtual void AddPoints(const char *pPlayer, int Points);
|
||||
|
||||
private:
|
||||
// copy of config vars
|
||||
char m_aFilename[512];
|
||||
bool m_Setup;
|
||||
|
||||
sqlite3 *m_pDb;
|
||||
sqlite3_stmt *m_pStmt;
|
||||
bool m_Done; // no more rows available for Step
|
||||
// returns true, if the query succeeded
|
||||
bool Execute(const char *pQuery);
|
||||
|
||||
void ExceptionOnError(int Result);
|
||||
|
||||
std::atomic_bool m_InUse;
|
||||
};
|
||||
|
||||
#endif // ENGINE_SERVER_DATABASES_SQLITE_H
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#define _WIN32_WINNT 0x0501
|
||||
|
||||
#include "server.h"
|
||||
|
||||
#include <base/math.h>
|
||||
#include <base/system.h>
|
||||
|
||||
|
@ -35,18 +37,15 @@
|
|||
#include <vector>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "databases/connection.h"
|
||||
#include "databases/connection_pool.h"
|
||||
#include "register.h"
|
||||
#include "server.h"
|
||||
|
||||
#if defined(CONF_FAMILY_WINDOWS)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <engine/server/databases/connection_pool.h>
|
||||
#include <engine/server/databases/mysql.h>
|
||||
#include <engine/server/databases/sqlite.h>
|
||||
|
||||
CSnapIDPool::CSnapIDPool()
|
||||
{
|
||||
Reset();
|
||||
|
@ -2358,7 +2357,7 @@ int CServer::Run()
|
|||
|
||||
if(g_Config.m_SvSqliteFile[0] != '\0')
|
||||
{
|
||||
auto pSqlServers = std::unique_ptr<CSqliteConnection>(new CSqliteConnection(
|
||||
auto pSqlServers = std::unique_ptr<IDbConnection>(CreateSqliteConnection(
|
||||
g_Config.m_SvSqliteFile, true));
|
||||
|
||||
if(g_Config.m_SvUseSQL)
|
||||
|
@ -2367,7 +2366,7 @@ int CServer::Run()
|
|||
}
|
||||
else
|
||||
{
|
||||
auto pCopy = std::unique_ptr<CSqliteConnection>(pSqlServers->Copy());
|
||||
auto pCopy = std::unique_ptr<IDbConnection>(pSqlServers->Copy());
|
||||
DbPool()->RegisterDatabase(std::move(pSqlServers), CDbConnectionPool::READ);
|
||||
DbPool()->RegisterDatabase(std::move(pCopy), CDbConnectionPool::WRITE);
|
||||
}
|
||||
|
@ -3166,14 +3165,20 @@ void CServer::ConAddSqlServer(IConsole::IResult *pResult, void *pUserData)
|
|||
|
||||
bool SetUpDb = pResult->NumArguments() == 8 ? pResult->GetInteger(7) : true;
|
||||
|
||||
auto pSqlServers = std::unique_ptr<CMysqlConnection>(new CMysqlConnection(
|
||||
auto pSqlServers = std::unique_ptr<IDbConnection>(CreateMysqlConnection(
|
||||
pResult->GetString(1), pResult->GetString(2), pResult->GetString(3),
|
||||
pResult->GetString(4), pResult->GetString(5), pResult->GetInteger(6),
|
||||
SetUpDb));
|
||||
|
||||
if(!pSqlServers)
|
||||
{
|
||||
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "can't add MySQL server: compiled without MySQL support");
|
||||
return;
|
||||
}
|
||||
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf),
|
||||
"Added new Sql%sServer: DB: '%s' Prefix: '%s' User: '%s' IP: <{'%s'}> Port: %d",
|
||||
"Added new Sql%sServer: DB: '%s' Prefix: '%s' User: '%s' IP: <{%s}> Port: %d",
|
||||
ReadOnly ? "Read" : "Write",
|
||||
pResult->GetString(1), pResult->GetString(2), pResult->GetString(3),
|
||||
pResult->GetString(5), pResult->GetInteger(6));
|
||||
|
@ -3483,6 +3488,11 @@ int main(int argc, const char **argv) // ignore_convention
|
|||
dbg_msg("secure", "could not initialize secure RNG");
|
||||
return -1;
|
||||
}
|
||||
if(MysqlInit() != 0)
|
||||
{
|
||||
dbg_msg("mysql", "failed to initialize MySQL library");
|
||||
return -1;
|
||||
}
|
||||
|
||||
CServer *pServer = CreateServer();
|
||||
IKernel *pKernel = IKernel::Create();
|
||||
|
@ -3556,6 +3566,8 @@ int main(int argc, const char **argv) // ignore_convention
|
|||
dbg_msg("server", "starting...");
|
||||
int Ret = pServer->Run();
|
||||
|
||||
MysqlUninit();
|
||||
|
||||
// free
|
||||
delete pKernel;
|
||||
|
||||
|
|
Loading…
Reference in a new issue