Cryptocurrency mining pool written in C++ for speed. Supports Stratum.
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

#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;
}
}