32#include <mysqld_error.h>
38 if (tokens.size() != 5 && tokens.size() != 6)
41 host.assign(tokens[0]);
43 user.assign(tokens[2]);
47 if (tokens.size() == 6)
48 ssl.assign(tokens[5]);
54 boost::asio::executor_work_guard<Trinity::Asio::IoContext::Executor>
WorkGuard;
61m_connectionInfo(connInfo),
62m_connectionFlags(connectionFlags)
93 mysqlInit = mysql_init(
nullptr);
97 return CR_UNKNOWN_ERROR;
101 char const* unix_socket =
nullptr;
104 mysql_options(mysqlInit, MYSQL_SET_CHARSET_NAME,
"utf8mb4");
112#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
113 unsigned int opt = MYSQL_PROTOCOL_PIPE;
117 unsigned int opt = MYSQL_PROTOCOL_SOCKET;
119 mysql_options(mysqlInit, MYSQL_OPT_PROTOCOL, (
char const*)&opt);
121#if !defined(MARIADB_VERSION_ID) && MYSQL_VERSION_ID >= 80000
129 mysql_options(mysqlInit, MYSQL_OPT_GET_SERVER_PUBLIC_KEY, (
char const*)&geterverPublicKey);
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<TransactionData>
const& queries = transaction->m_queries;
397 for (
auto itr = queries.begin(); itr != queries.end(); ++itr)
401 TC_LOG_WARN(
"sql.sql",
"Transaction aborted. {} queries not executed.", queries.size());
419 return mysql_real_escape_string(
m_Mysql, to, from, length);
434 boost::asio::executor_work_guard executorWorkGuard = boost::asio::make_work_guard(context->
get_executor());
438 .WorkGuard = std::move(executorWorkGuard)
462 return mysql_get_server_version(
m_Mysql);
467 ASSERT(index <
m_stmts.size(),
"Tried to access invalid prepared statement index %u (max index " SZFMTD ") on database `%s`, connection type: %s",
471 TC_LOG_ERROR(
"sql.sql",
"Could not fetch prepared statement {} on database `{}`, connection type: {}.",
488 MYSQL_STMT* stmt = mysql_stmt_init(
m_Mysql);
491 TC_LOG_ERROR(
"sql.sql",
"In mysql_stmt_init() id: {}, sql: \"{}\"", index, sql);
497 if (mysql_stmt_prepare(stmt, sql.data(),
static_cast<unsigned long>(sql.size())))
499 TC_LOG_ERROR(
"sql.sql",
"In mysql_stmt_prepare() id: {}, sql: \"{}\"", index, sql);
501 mysql_stmt_close(stmt);
505 m_stmts[index] = std::make_unique<MySQLPreparedStatement>(
reinterpret_cast<MySQLStmt*
>(stmt), std::string(sql));
516 if (!
_Query(stmt, &mysqlStmt, &result, &rowCount, &fieldCount))
519 if (mysql_more_results(
m_Mysql))
530 case CR_SERVER_GONE_ERROR:
532 case CR_SERVER_LOST_EXTENDED:
536 TC_LOG_ERROR(
"sql.sql",
"Lost the connection to the MySQL server!");
543 case CR_CONN_HOST_ERROR:
545 TC_LOG_INFO(
"sql.sql",
"Attempting to reconnect to the MySQL server...");
555 TC_LOG_FATAL(
"sql.sql",
"Could not re-prepare statements!");
556 std::this_thread::sleep_for(std::chrono::seconds(10));
560 TC_LOG_INFO(
"sql.sql",
"Successfully reconnected to {} @{}:{} ({}).",
568 if ((--attempts) == 0)
572 TC_LOG_FATAL(
"sql.sql",
"Failed to reconnect to the MySQL server, "
573 "terminating the server to prevent data corruption!");
576 std::this_thread::sleep_for(std::chrono::seconds(10));
583 std::this_thread::sleep_for(std::chrono::seconds(3));
588 case ER_LOCK_DEADLOCK:
591 case ER_WRONG_VALUE_COUNT:
596 case ER_BAD_FIELD_ERROR:
597 case ER_NO_SUCH_TABLE:
598 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.");
599 std::this_thread::sleep_for(std::chrono::seconds(10));
603 TC_LOG_ERROR(
"sql.sql",
"Error while parsing SQL. Core fix required.");
604 std::this_thread::sleep_for(std::chrono::seconds(10));
608 TC_LOG_ERROR(
"sql.sql",
"Unhandled MySQL errno {}. Unexpected behaviour possible.", errNo);
#define TC_LOG_DEBUG(filterType__, message__,...)
#define TC_LOG_ERROR(filterType__, message__,...)
#define TC_LOG_FATAL(filterType__, message__,...)
#define TC_LOG_INFO(filterType__, message__,...)
#define TC_LOG_WARN(filterType__, message__,...)
std::remove_pointer_t< decltype(std::declval< MYSQL_BIND >().is_null)> MySQLBool
uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime)
std::unique_ptr< WorkerThread > m_workerThread
Core worker thread.
std::thread::id GetWorkerThreadId() const
bool Execute(char const *sql)
void PrepareStatement(uint32 index, std::string_view sql, ConnectionFlags flags)
MySQLPreparedStatement * GetPreparedStatement(uint32 index)
int ExecuteTransaction(std::shared_ptr< TransactionBase > transaction)
MySQLConnectionInfo & m_connectionInfo
Connection info (used for logging)
void RollbackTransaction()
PreparedStatementContainer m_stmts
PreparedStatements storage.
size_t EscapeString(char *to, const char *from, size_t length)
uint32 GetServerVersion() const
MySQLConnection(MySQLConnectionInfo &connInfo, ConnectionFlags connectionFlags)
ResultSet * Query(char const *sql)
bool _Query(char const *sql, MySQLResult **pResult, MySQLField **pFields, uint64 *pRowCount, uint32 *pFieldCount)
ConnectionFlags m_connectionFlags
Connection flags (for preparing relevant statements)
bool _HandleMySQLErrno(uint32 errNo, uint8 attempts=5)
void Unlock()
Called by parent databasepool. Will let other threads access this connection.
bool m_prepareError
Was there any error while preparing statements?
MySQLHandle * m_Mysql
MySQL Handle.
virtual ~MySQLConnection()
virtual void DoPrepareStatements()=0
void StartWorkerThread(Trinity::Asio::IoContext *context)
bool m_reconnecting
Are we reconnecting?
std::string getQueryString() const
void BindParameters(PreparedStatementBase *stmt)
Executor get_executor() noexcept
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)
boost::asio::executor_work_guard< Trinity::Asio::IoContext::Executor > WorkGuard
static PreparedStatementBase * ToExecutable(std::unique_ptr< PreparedStatementBase > const &stmt)