13#include <mysqld_error.h>
26MySQLConnection::MySQLConnection(MySQLConnectionInfo& connInfo) :
27 m_reconnecting(false),
28 m_prepareError(false),
32 m_connectionInfo(connInfo),
33 m_connectionFlags(CONNECTION_SYNCH) { }
36 m_reconnecting(false),
37 m_prepareError(false),
40 m_connectionInfo(connInfo),
41 m_connectionFlags(CONNECTION_ASYNC)
46MySQLConnection::~MySQLConnection()
50 for (
size_t i = 0; i < m_stmts.size(); ++i)
56void MySQLConnection::Close()
62bool MySQLConnection::Open()
65 mysqlInit = mysql_init(NULL);
68 SF_LOG_ERROR(
"sql.sql",
"Could not initialize Mysql connection to database `%s`", m_connectionInfo._database.c_str());
73 char const* unix_socket;
76 mysql_options(mysqlInit, MYSQL_SET_CHARSET_NAME,
"utf8");
79 if (m_connectionInfo._host ==
".")
81 unsigned int opt = MYSQL_PROTOCOL_PIPE;
82 mysql_options(mysqlInit, MYSQL_OPT_PROTOCOL, (
char const*)&opt);
88 port = atoi(m_connectionInfo._port_or_socket.c_str());
92 if (m_connectionInfo._host ==
".")
94 unsigned int opt = MYSQL_PROTOCOL_SOCKET;
95 mysql_options(mysqlInit, MYSQL_OPT_PROTOCOL, (
char const*)&opt);
96 m_connectionInfo._host =
"localhost";
98 unix_socket = m_connectionInfo._port_or_socket.c_str();
102 port = atoi(m_connectionInfo._port_or_socket.c_str());
107 m_Mysql = mysql_real_connect(mysqlInit, m_connectionInfo._host.c_str(), m_connectionInfo._user.c_str(),
108 m_connectionInfo._password.c_str(), m_connectionInfo._database.c_str(), port, unix_socket, 0);
114 SF_LOG_INFO(
"sql.sql",
"MySQL client library: %s", mysql_get_client_info());
115 SF_LOG_INFO(
"sql.sql",
"MySQL server ver: %s ", mysql_get_server_info(m_Mysql));
121 SF_LOG_INFO(
"sql.sql",
"Connected to MySQL database at %s", m_connectionInfo._host.c_str());
122 mysql_autocommit(m_Mysql, 1);
126 mysql_set_character_set(m_Mysql,
"utf8");
127 return PrepareStatements();
131 SF_LOG_ERROR(
"sql.sql",
"Could not connect to MySQL database at %s: %s\n", m_connectionInfo._host.c_str(), mysql_error(mysqlInit));
132 mysql_close(mysqlInit);
137bool MySQLConnection::PrepareStatements()
139 DoPrepareStatements();
140 return !m_prepareError;
143bool MySQLConnection::Execute(
const char* sql)
151 if (mysql_query(m_Mysql, sql))
153 uint32 lErrno = mysql_errno(m_Mysql);
156 SF_LOG_ERROR(
"sql.sql",
"[%u] %s", lErrno, mysql_error(m_Mysql));
158 if (_HandleMySQLErrno(lErrno))
177 MySQLPreparedStatement* m_mStmt = GetPreparedStatement(index);
184 MYSQL_STMT* msql_STMT = m_mStmt->
GetSTMT();
185 MYSQL_BIND* msql_BIND = m_mStmt->
GetBind();
189#if MYSQL_VERSION_ID >= 80300
190 if (mysql_stmt_bind_named_param(msql_STMT, msql_BIND, m_mStmt->
m_paramCount,
nullptr))
192 if (mysql_stmt_bind_param(msql_STMT, msql_BIND))
195 uint32 lErrno = mysql_errno(m_Mysql);
196 SF_LOG_ERROR(
"sql.sql",
"SQL(p): %s\n [ERROR]: [%u] %s", m_mStmt->
getQueryString(m_queries[index].first).c_str(), lErrno, mysql_stmt_error(msql_STMT));
198 if (_HandleMySQLErrno(lErrno))
199 return Execute(stmt);
205 if (mysql_stmt_execute(msql_STMT))
207 uint32 lErrno = mysql_errno(m_Mysql);
208 SF_LOG_ERROR(
"sql.sql",
"SQL(p): %s\n [ERROR]: [%u] %s", m_mStmt->
getQueryString(m_queries[index].first).c_str(), lErrno, mysql_stmt_error(msql_STMT));
210 if (_HandleMySQLErrno(lErrno))
211 return Execute(stmt);
231 MySQLPreparedStatement* m_mStmt = GetPreparedStatement(index);
238 MYSQL_STMT* msql_STMT = m_mStmt->
GetSTMT();
239 MYSQL_BIND* msql_BIND = m_mStmt->
GetBind();
243#if MYSQL_VERSION_ID >= 80300
244 if (mysql_stmt_bind_named_param(msql_STMT, msql_BIND, m_mStmt->
m_paramCount,
nullptr))
246 if (mysql_stmt_bind_param(msql_STMT, msql_BIND))
249 uint32 lErrno = mysql_errno(m_Mysql);
250 SF_LOG_ERROR(
"sql.sql",
"SQL(p): %s\n [ERROR]: [%u] %s", m_mStmt->
getQueryString(m_queries[index].first).c_str(), lErrno, mysql_stmt_error(msql_STMT));
252 if (_HandleMySQLErrno(lErrno))
253 return _Query(stmt, pResult, pRowCount, pFieldCount);
259 if (mysql_stmt_execute(msql_STMT))
261 uint32 lErrno = mysql_errno(m_Mysql);
262 SF_LOG_ERROR(
"sql.sql",
"SQL(p): %s\n [ERROR]: [%u] %s",
263 m_mStmt->
getQueryString(m_queries[index].first).c_str(), lErrno, mysql_stmt_error(msql_STMT));
265 if (_HandleMySQLErrno(lErrno))
266 return _Query(stmt, pResult, pRowCount, pFieldCount);
276 *pResult = mysql_stmt_result_metadata(msql_STMT);
277 *pRowCount = mysql_stmt_num_rows(msql_STMT);
278 *pFieldCount = mysql_stmt_field_count(msql_STMT);
284ResultSet* MySQLConnection::Query(
const char* sql)
289 MYSQL_RES* result = NULL;
290 MYSQL_FIELD* fields = NULL;
294 if (!_Query(sql, &result, &fields, &rowCount, &fieldCount))
297 return new ResultSet(result, fields, rowCount, fieldCount);
300bool MySQLConnection::_Query(
const char* sql, MYSQL_RES** pResult, MYSQL_FIELD** pFields,
uint64* pRowCount,
uint32* pFieldCount)
308 if (mysql_query(m_Mysql, sql))
310 uint32 lErrno = mysql_errno(m_Mysql);
312 SF_LOG_ERROR(
"sql.sql",
"[%u] %s", lErrno, mysql_error(m_Mysql));
314 if (_HandleMySQLErrno(lErrno))
315 return _Query(sql, pResult, pFields, pRowCount, pFieldCount);
322 *pResult = mysql_store_result(m_Mysql);
323 *pRowCount = mysql_affected_rows(m_Mysql);
324 *pFieldCount = mysql_field_count(m_Mysql);
332 mysql_free_result(*pResult);
336 *pFields = mysql_fetch_fields(*pResult);
341void MySQLConnection::BeginTransaction()
343 Execute(
"START TRANSACTION");
346void MySQLConnection::RollbackTransaction()
351void MySQLConnection::CommitTransaction()
356bool MySQLConnection::ExecuteTransaction(
SQLTransaction& transaction)
358 std::list<SQLElementData>
const& queries = transaction->m_queries;
364 std::list<SQLElementData>::const_iterator itr;
365 for (itr = queries.begin(); itr != queries.end(); ++itr)
367 SQLElementData
const& data = *itr;
376 SF_LOG_WARN(
"sql.sql",
"Transaction aborted. %u queries not executed.", (
uint32)queries.size());
377 RollbackTransaction();
388 SF_LOG_WARN(
"sql.sql",
"Transaction aborted. %u queries not executed.", (
uint32)queries.size());
389 RollbackTransaction();
408 ASSERT(index < m_stmts.size());
409 MySQLPreparedStatement* ret = m_stmts[index];
411 SF_LOG_ERROR(
"sql.sql",
"Could not fetch prepared statement %u on database `%s`, connection type: %s.",
412 index, m_connectionInfo._database.c_str(), (m_connectionFlags & CONNECTION_ASYNC) ?
"asynchronous" :
"synchronous");
417void MySQLConnection::PrepareStatement(
uint32 index, std::string sql, ConnectionFlags flags)
419 m_queries.insert(PreparedStatementMap::value_type(index, std::make_pair(sql, flags)));
423 delete m_stmts[index];
428 if (!(m_connectionFlags & flags))
430 m_stmts[index] = NULL;
434 MYSQL_STMT* stmt = mysql_stmt_init(m_Mysql);
437 SF_LOG_ERROR(
"sql.sql",
"In mysql_stmt_init() id: %u, sql: \"%s\"", index, sql.c_str());
439 m_prepareError =
true;
443 if (mysql_stmt_prepare(stmt, sql.c_str(), sql.length()))
445 SF_LOG_ERROR(
"sql.sql",
"In mysql_stmt_prepare() id: %u, sql: \"%s\"", index, sql.c_str());
447 mysql_stmt_close(stmt);
448 m_prepareError =
true;
452 MySQLPreparedStatement* mStmt =
new MySQLPreparedStatement(stmt);
453 m_stmts[index] = mStmt;
460 MYSQL_RES* result = NULL;
464 if (!_Query(stmt, &result, &rowCount, &fieldCount))
467 if (mysql_more_results(m_Mysql))
469 mysql_next_result(m_Mysql);
471 return new PreparedResultSet(stmt->
m_stmt->
GetSTMT(), result, rowCount, fieldCount);
474bool MySQLConnection::_HandleMySQLErrno(
uint32 errNo)
478 case CR_SERVER_GONE_ERROR:
480 case CR_INVALID_CONN_HANDLE:
481 case CR_SERVER_LOST_EXTENDED:
483 m_reconnecting =
true;
484 uint64 oldThreadId = mysql_thread_id(GetHandle());
485 mysql_close(GetHandle());
488 SF_LOG_INFO(
"sql.sql",
"Connection to the MySQL server is active.");
489 if (oldThreadId != mysql_thread_id(GetHandle()))
490 SF_LOG_INFO(
"sql.sql",
"Successfully reconnected to %s @%s:%s (%s).",
491 m_connectionInfo._database.c_str(), m_connectionInfo._host.c_str(), m_connectionInfo._port_or_socket.c_str(),
492 (m_connectionFlags & CONNECTION_ASYNC) ?
"asynchronous" :
"synchronous");
494 m_reconnecting =
false;
498 uint32 lErrno = mysql_errno(GetHandle());
500 return _HandleMySQLErrno(lErrno);
503 case ER_LOCK_DEADLOCK:
506 case ER_WRONG_VALUE_COUNT:
511 case ER_BAD_FIELD_ERROR:
512 case ER_NO_SUCH_TABLE:
513 SF_LOG_ERROR(
"sql.sql",
"Your database structure is not up to date. Please make sure you've executed all queries in the sql/updates folders.");
518 SF_LOG_ERROR(
"sql.sql",
"Error while parsing SQL. Core fix required.");
523 SF_LOG_ERROR(
"sql.sql",
"Unhandled MySQL errno %u. Unexpected behaviour possible.", errNo);
#define SF_LOG_DEBUG(filterType__,...)
#define SF_LOG_WARN(filterType__,...)
#define SF_LOG_ERROR(filterType__,...)
#define SF_LOG_INFO(filterType__,...)
uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime)
Skyfire::AutoPtr< Transaction, Skyfire::Mutex > SQLTransaction
PreparedStatement * m_stmt
std::string getQueryString(std::string const &sqlPattern) const
MySQLPreparedStatement * m_stmt
void SleepForSeconds(uint32 seconds)