31#include <mysqld_error.h>
37 if (tokens.size() != 5 && tokens.size() != 6)
40 host.assign(tokens[0]);
42 user.assign(tokens[2]);
46 if (tokens.size() == 6)
47 ssl.assign(tokens[5]);
55m_connectionInfo(connInfo),
63m_connectionInfo(connInfo),
91 mysqlInit = mysql_init(
nullptr);
95 return CR_UNKNOWN_ERROR;
99 char const* unix_socket;
102 mysql_options(mysqlInit, MYSQL_SET_CHARSET_NAME,
"utf8mb4");
107 unsigned int opt = MYSQL_PROTOCOL_PIPE;
108 mysql_options(mysqlInit, MYSQL_OPT_PROTOCOL, (
char const*)&opt);
120 unsigned int opt = MYSQL_PROTOCOL_SOCKET;
121 mysql_options(mysqlInit, MYSQL_OPT_PROTOCOL, (
char const*)&opt);
129 unix_socket =
nullptr;
135#if !defined(MARIADB_VERSION_ID) && MYSQL_VERSION_ID >= 80000
136 mysql_ssl_mode opt_use_ssl = SSL_MODE_DISABLED;
139 opt_use_ssl = SSL_MODE_REQUIRED;
141 mysql_options(mysqlInit, MYSQL_OPT_SSL_MODE, (
char const*)&opt_use_ssl);
148 mysql_options(mysqlInit, MYSQL_OPT_SSL_ENFORCE, (
char const*)&opt_use_ssl);
159 TC_LOG_INFO(
"sql.sql",
"MySQL client library: {}", mysql_get_client_info());
171 mysql_set_character_set(
m_Mysql,
"utf8mb4");
177 uint32 errorCode = mysql_errno(mysqlInit);
178 mysql_close(mysqlInit);
228 MYSQL_STMT* msql_STMT = m_mStmt->
GetSTMT();
229 MYSQL_BIND* msql_BIND = m_mStmt->
GetBind();
233 if (mysql_stmt_bind_param(msql_STMT, msql_BIND))
245 if (mysql_stmt_execute(msql_STMT))
274 *mysqlStmt = m_mStmt;
276 MYSQL_STMT* msql_STMT = m_mStmt->
GetSTMT();
277 MYSQL_BIND* msql_BIND = m_mStmt->
GetBind();
281 if (mysql_stmt_bind_param(msql_STMT, msql_BIND))
287 return _Query(stmt, mysqlStmt, pResult, pRowCount, pFieldCount);
293 if (mysql_stmt_execute(msql_STMT))
296 TC_LOG_ERROR(
"sql.sql",
"SQL(p): {}\n [ERROR]: [{}] {}",
300 return _Query(stmt, mysqlStmt, pResult, pRowCount, pFieldCount);
310 *pResult =
reinterpret_cast<MySQLResult*
>(mysql_stmt_result_metadata(msql_STMT));
311 *pRowCount = mysql_stmt_num_rows(msql_STMT);
312 *pFieldCount = mysql_stmt_field_count(msql_STMT);
327 if (!
_Query(sql, &result, &fields, &rowCount, &fieldCount))
330 return new ResultSet(result, fields, rowCount, fieldCount);
348 return _Query(sql, pResult, pFields, pRowCount, pFieldCount);
356 *pRowCount = mysql_affected_rows(
m_Mysql);
357 *pFieldCount = mysql_field_count(
m_Mysql);
365 mysql_free_result(*pResult);
369 *pFields =
reinterpret_cast<MySQLField*
>(mysql_fetch_fields(*pResult));
391 std::vector<SQLElementData>
const& queries = transaction->m_queries;
397 for (
auto itr = queries.begin(); itr != queries.end(); ++itr)
408 TC_LOG_WARN(
"sql.sql",
"Transaction aborted. {} queries not executed.", (
uint32)queries.size());
421 TC_LOG_WARN(
"sql.sql",
"Transaction aborted. {} queries not executed.", (
uint32)queries.size());
442 return mysql_real_escape_string(
m_Mysql, to, from, length);
467 return mysql_get_server_version(
m_Mysql);
472 ASSERT(index <
m_stmts.size(),
"Tried to access invalid prepared statement index %u (max index " SZFMTD ") on database `%s`, connection type: %s",
476 TC_LOG_ERROR(
"sql.sql",
"Could not fetch prepared statement {} on database `{}`, connection type: {}.",
493 MYSQL_STMT* stmt = mysql_stmt_init(
m_Mysql);
496 TC_LOG_ERROR(
"sql.sql",
"In mysql_stmt_init() id: {}, sql: \"{}\"", index, sql);
502 if (mysql_stmt_prepare(stmt, sql.c_str(),
static_cast<unsigned long>(sql.size())))
504 TC_LOG_ERROR(
"sql.sql",
"In mysql_stmt_prepare() id: {}, sql: \"{}\"", index, sql);
506 mysql_stmt_close(stmt);
510 m_stmts[index] = std::make_unique<MySQLPreparedStatement>(
reinterpret_cast<MySQLStmt*
>(stmt), sql);
521 if (!
_Query(stmt, &mysqlStmt, &result, &rowCount, &fieldCount))
524 if (mysql_more_results(
m_Mysql))
535 case CR_SERVER_GONE_ERROR:
537 case CR_SERVER_LOST_EXTENDED:
541 TC_LOG_ERROR(
"sql.sql",
"Lost the connection to the MySQL server!");
548 case CR_CONN_HOST_ERROR:
550 TC_LOG_INFO(
"sql.sql",
"Attempting to reconnect to the MySQL server...");
560 TC_LOG_FATAL(
"sql.sql",
"Could not re-prepare statements!");
561 std::this_thread::sleep_for(std::chrono::seconds(10));
565 TC_LOG_INFO(
"sql.sql",
"Successfully reconnected to {} @{}:{} ({}).",
573 if ((--attempts) == 0)
577 TC_LOG_FATAL(
"sql.sql",
"Failed to reconnect to the MySQL server, "
578 "terminating the server to prevent data corruption!");
581 std::this_thread::sleep_for(std::chrono::seconds(10));
588 std::this_thread::sleep_for(std::chrono::seconds(3));
593 case ER_LOCK_DEADLOCK:
596 case ER_WRONG_VALUE_COUNT:
601 case ER_BAD_FIELD_ERROR:
602 case ER_NO_SUCH_TABLE:
603 TC_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.");
604 std::this_thread::sleep_for(std::chrono::seconds(10));
608 TC_LOG_ERROR(
"sql.sql",
"Error while parsing SQL. Core fix required.");
609 std::this_thread::sleep_for(std::chrono::seconds(10));
613 TC_LOG_ERROR(
"sql.sql",
"Unhandled MySQL errno {}. Unexpected behaviour possible.", errNo);
#define TC_LOG_WARN(filterType__,...)
#define TC_LOG_DEBUG(filterType__,...)
#define TC_LOG_ERROR(filterType__,...)
#define TC_LOG_INFO(filterType__,...)
#define TC_LOG_FATAL(filterType__,...)
std::remove_pointer_t< decltype(std::declval< MYSQL_BIND >().is_null)> MySQLBool
uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime)
bool Execute(char const *sql)
void PrepareStatement(uint32 index, std::string const &sql, ConnectionFlags flags)
MySQLPreparedStatement * GetPreparedStatement(uint32 index)
int ExecuteTransaction(std::shared_ptr< TransactionBase > transaction)
ProducerConsumerQueue< SQLOperation * > * m_queue
std::unique_ptr< DatabaseWorker > m_worker
Queue shared with other asynchronous connections.
MySQLConnectionInfo & m_connectionInfo
MySQL Handle.
void RollbackTransaction()
PreparedStatementContainer m_stmts
size_t EscapeString(char *to, const char *from, size_t length)
uint32 GetServerVersion() const
MySQLConnection(MySQLConnectionInfo &connInfo)
ResultSet * Query(char const *sql)
bool _Query(char const *sql, MySQLResult **pResult, MySQLField **pFields, uint64 *pRowCount, uint32 *pFieldCount)
ConnectionFlags m_connectionFlags
Connection info (used for logging)
bool _HandleMySQLErrno(uint32 errNo, uint8 attempts=5)
Was there any error while preparing statements?
void Unlock()
Called by parent databasepool. Will let other threads access this connection.
std::mutex m_Mutex
Connection flags (for preparing relevant statements)
bool m_prepareError
Are we reconnecting?
MySQLHandle * m_Mysql
Core worker task.
virtual ~MySQLConnection()
Constructor for asynchronous connections.
virtual void DoPrepareStatements()=0
bool m_reconnecting
PreparedStatements storage.
std::string getQueryString() const
void BindParameters(PreparedStatementBase *stmt)
TC_COMMON_API std::vector< std::string_view > Tokenize(std::string_view str, char sep, bool keepEmpty)
std::string port_or_socket
MySQLConnectionInfo(std::string const &infoString)
PreparedStatementBase * stmt