mirror of https://github.com/GOSTSec/poolserver
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
292 lines
9.1 KiB
292 lines
9.1 KiB
#include "DatabaseConnection.h" |
|
#include "Log.h" |
|
#include "Util.h" |
|
|
|
namespace MySQL |
|
{ |
|
DatabaseConnection::DatabaseConnection(ConnectionInfo connInfo, DatabaseWorkQueue* asyncQueue) : |
|
_mysql(NULL), _asyncQueue(asyncQueue), _worker(NULL), _connectionInfo(connInfo) |
|
{ |
|
if (_asyncQueue) { |
|
_worker = new DatabaseWorker(_asyncQueue, this); |
|
Type = MYSQL_CONN_ASYNC; |
|
} else |
|
Type = MYSQL_CONN_SYNC; |
|
} |
|
|
|
DatabaseConnection::~DatabaseConnection() |
|
{ |
|
assert(_mysql); |
|
|
|
for (uint32_t i = 0; i < _stmts.size(); ++i) |
|
delete _stmts[i]; |
|
|
|
mysql_close(_mysql); |
|
} |
|
|
|
bool DatabaseConnection::Open() |
|
{ |
|
MYSQL* mysqlInit; |
|
mysqlInit = mysql_init(NULL); |
|
if (!mysqlInit) |
|
throw ConnectionException(Util::FS("Could not initialize Mysql connection to database `%s`", _connectionInfo.DB.c_str())); |
|
|
|
mysql_options(mysqlInit, MYSQL_SET_CHARSET_NAME, "utf8"); |
|
|
|
_mysql = mysql_real_connect(mysqlInit, _connectionInfo.Host.c_str(), _connectionInfo.User.c_str(), |
|
_connectionInfo.Pass.c_str(), _connectionInfo.DB.c_str(), _connectionInfo.Port, NULL, 0); |
|
|
|
if (_mysql) |
|
{ |
|
sLog.Info(LOG_DATABASE, "Connected to MySQL database at %s", _connectionInfo.Host.c_str()); |
|
mysql_autocommit(_mysql, 1); |
|
|
|
// set connection properties to UTF8 to properly handle locales for different |
|
// server configs - core sends data in UTF8, so MySQL must expect UTF8 too |
|
mysql_set_character_set(_mysql, "utf8"); |
|
|
|
return true; |
|
} |
|
else |
|
{ |
|
const char* error = mysql_error(mysqlInit); |
|
mysql_close(mysqlInit); |
|
throw ConnectionException(Util::FS("Could not connect to MySQL database at %s: %s", _connectionInfo.Host.c_str(), mysql_error(mysqlInit))); |
|
} |
|
} |
|
|
|
void DatabaseConnection::Close() |
|
{ |
|
delete this; |
|
} |
|
|
|
bool DatabaseConnection::Execute(const char* query) |
|
{ |
|
sLog.Debug(LOG_DATABASE, "DatabaseConnectionMySQL::Execute(): %s", query); |
|
|
|
if (!query || !_mysql) |
|
return false; |
|
|
|
if (mysql_query(_mysql, query)) |
|
{ |
|
uint32_t lErrno = mysql_errno(_mysql); |
|
|
|
sLog.Error(LOG_DATABASE, "[%u] %s", lErrno, mysql_error(_mysql)); |
|
|
|
if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection) |
|
return Execute(query); // Try again |
|
|
|
return false; |
|
} |
|
else |
|
return true; |
|
} |
|
|
|
ResultSet* DatabaseConnection::Query(const char* query) |
|
{ |
|
sLog.Debug(LOG_DATABASE, "DatabaseConnectionMySQL::Query(): %s", query); |
|
|
|
if (!query) |
|
return NULL; |
|
|
|
MYSQL_RES *result = NULL; |
|
MYSQL_FIELD *fields = NULL; |
|
uint64_t rowCount = 0; |
|
uint32_t fieldCount = 0; |
|
|
|
if (!_Query(query, &result, &fields, rowCount, fieldCount)) |
|
return NULL; |
|
|
|
return new ResultSet(result, fields, rowCount, fieldCount); |
|
} |
|
|
|
bool DatabaseConnection::_Query(const char *query, MYSQL_RES** result, MYSQL_FIELD** fields, uint64_t& rowCount, uint32_t& fieldCount) |
|
{ |
|
if (!_mysql) |
|
return false; |
|
|
|
if (mysql_query(_mysql, query)) |
|
{ |
|
uint32_t lErrno = mysql_errno(_mysql); |
|
|
|
sLog.Error(LOG_DATABASE, "[%u] %s", lErrno, mysql_error(_mysql)); |
|
|
|
if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection) |
|
return _Query(query, result, fields, rowCount, fieldCount); // We try again |
|
|
|
return false; |
|
} |
|
|
|
*result = mysql_store_result(_mysql); |
|
rowCount = mysql_affected_rows(_mysql); |
|
fieldCount = mysql_field_count(_mysql); |
|
|
|
if (!*result) |
|
return false; |
|
|
|
if (!rowCount) { |
|
mysql_free_result(*result); |
|
return false; |
|
} |
|
|
|
*fields = mysql_fetch_fields(*result); |
|
|
|
return true; |
|
} |
|
|
|
bool DatabaseConnection::_Query(PreparedStatement* stmt, MYSQL_RES** result, MYSQL_STMT** resultSTMT, uint32& fieldCount) |
|
{ |
|
if (!_mysql) |
|
return false; |
|
|
|
ConnectionPreparedStatement* cstmt = GetPreparedStatement(stmt->_index); |
|
|
|
if (!cstmt) { |
|
sLog.Error(LOG_DATABASE, "STMT id: %u not found!", stmt->_index); |
|
return false; |
|
} |
|
|
|
cstmt->BindParameters(stmt); |
|
|
|
MYSQL_STMT* mSTMT = cstmt->GetSTMT(); |
|
MYSQL_BIND* mBIND = cstmt->GetBind(); |
|
|
|
if (mysql_stmt_bind_param(mSTMT, mBIND)) |
|
{ |
|
uint32 lErrno = mysql_errno(_mysql); |
|
sLog.Error(LOG_DATABASE, "STMT Execute Error[%u]: %s", lErrno, mysql_stmt_error(mSTMT)); |
|
|
|
if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection) |
|
return Execute(stmt); // Try again |
|
|
|
cstmt->ClearParameters(); |
|
return false; |
|
} |
|
|
|
if (mysql_stmt_execute(mSTMT)) |
|
{ |
|
uint32 lErrno = mysql_errno(_mysql); |
|
sLog.Error(LOG_DATABASE, "STMT Execute Error[%u]: %s", lErrno, mysql_stmt_error(mSTMT)); |
|
|
|
if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection) |
|
return _Query(stmt, result, resultSTMT, fieldCount); // Try again |
|
|
|
cstmt->ClearParameters(); |
|
return false; |
|
} |
|
|
|
cstmt->ClearParameters(); |
|
|
|
*result = mysql_stmt_result_metadata(mSTMT); |
|
fieldCount = mysql_stmt_field_count(mSTMT); |
|
*resultSTMT = mSTMT; |
|
|
|
return true; |
|
} |
|
|
|
bool DatabaseConnection::Execute(PreparedStatement* stmt) |
|
{ |
|
if (!_mysql) |
|
return false; |
|
|
|
ConnectionPreparedStatement* cstmt = GetPreparedStatement(stmt->_index); |
|
|
|
if (!cstmt) { |
|
sLog.Error(LOG_DATABASE, "STMT id: %u not found!", stmt->_index); |
|
return false; |
|
} |
|
|
|
cstmt->BindParameters(stmt); |
|
|
|
MYSQL_STMT* mSTMT = cstmt->GetSTMT(); |
|
MYSQL_BIND* mBIND = cstmt->GetBind(); |
|
|
|
if (mysql_stmt_bind_param(mSTMT, mBIND)) |
|
{ |
|
uint32 lErrno = mysql_errno(_mysql); |
|
sLog.Error(LOG_DATABASE, "STMT Execute Error[%u]: %s", lErrno, mysql_stmt_error(mSTMT)); |
|
|
|
if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection) |
|
return Execute(stmt); // Try again |
|
|
|
cstmt->ClearParameters(); |
|
return false; |
|
} |
|
|
|
if (mysql_stmt_execute(mSTMT)) |
|
{ |
|
uint32 lErrno = mysql_errno(_mysql); |
|
sLog.Error(LOG_DATABASE, "STMT Execute Error[%u]: %s", lErrno, mysql_stmt_error(mSTMT)); |
|
|
|
if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection) |
|
return Execute(stmt); // Try again |
|
|
|
cstmt->ClearParameters(); |
|
return false; |
|
} |
|
|
|
cstmt->ClearParameters(); |
|
|
|
return true; |
|
} |
|
|
|
ResultSet* DatabaseConnection::Query(PreparedStatement* stmt) |
|
{ |
|
MYSQL_RES* result = NULL; |
|
MYSQL_STMT* resultSTMT = NULL; |
|
uint32 fieldCount = 0; |
|
|
|
if (!_Query(stmt, &result, &resultSTMT, fieldCount)) |
|
return NULL; |
|
|
|
if (mysql_more_results(_mysql)) |
|
mysql_next_result(_mysql); |
|
|
|
return new ResultSet(result, resultSTMT, fieldCount); |
|
} |
|
|
|
bool DatabaseConnection::PrepareStatement(uint32 index, const char* sql) |
|
{ |
|
if (!_mysql) |
|
return false; |
|
|
|
// For reconnection case |
|
//if (m_reconnecting) |
|
// delete m_stmts[index]; |
|
|
|
MYSQL_STMT* stmt = mysql_stmt_init(_mysql); |
|
|
|
if (!stmt) { |
|
sLog.Error(LOG_DATABASE, "In mysql_stmt_init() id: %u, sql: \"%s\"", index, sql); |
|
sLog.Error(LOG_DATABASE, "%s", mysql_error(_mysql)); |
|
return false; |
|
} |
|
|
|
if (mysql_stmt_prepare(stmt, sql, strlen(sql))) { |
|
sLog.Error(LOG_DATABASE, "In mysql_stmt_init() id: %u, sql: \"%s\"", index, sql); |
|
sLog.Error(LOG_DATABASE, "%s", mysql_stmt_error(stmt)); |
|
mysql_stmt_close(stmt); |
|
return false; |
|
} |
|
|
|
// Set flags to update max_length property |
|
my_bool mysql_c_api_sucks = true; |
|
mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*)&mysql_c_api_sucks); |
|
|
|
// Resize stmt vector |
|
if (index >= _stmts.size()) |
|
_stmts.resize(index+1); |
|
|
|
ConnectionPreparedStatement* cstmt = new ConnectionPreparedStatement(stmt); |
|
_stmts[index] = cstmt; |
|
|
|
sLog.Debug(LOG_DATABASE, "Prepared STMT id: %u, sql: \"%s\"", index, sql); |
|
|
|
return true; |
|
} |
|
|
|
bool DatabaseConnection::_HandleMySQLErrno(uint32_t lErrno) |
|
{ |
|
return false; |
|
} |
|
}
|
|
|