AzerothCore 3.3.5a
OpenSource WoW Emulator
Loading...
Searching...
No Matches
MySQLConnection Class Referenceabstract

#include "MySQLConnection.h"

Inheritance diagram for MySQLConnection:
CharacterDatabaseConnection LoginDatabaseConnection WorldDatabaseConnection

Public Member Functions

 MySQLConnection (MySQLConnectionInfo &connInfo)
 
 MySQLConnection (ProducerConsumerQueue< SQLOperation * > *queue, MySQLConnectionInfo &connInfo)
 Constructor for synchronous connections.
 
virtual ~MySQLConnection ()
 Constructor for asynchronous connections.
 
virtual uint32 Open ()
 
void Close ()
 
bool PrepareStatements ()
 
bool Execute (std::string_view sql)
 
bool Execute (PreparedStatementBase *stmt)
 
ResultSet * Query (std::string_view sql)
 
PreparedResultSet * Query (PreparedStatementBase *stmt)
 
bool _Query (std::string_view sql, MySQLResult **pResult, MySQLField **pFields, uint64 *pRowCount, uint32 *pFieldCount)
 
bool _Query (PreparedStatementBase *stmt, MySQLPreparedStatement **mysqlStmt, MySQLResult **pResult, uint64 *pRowCount, uint32 *pFieldCount)
 
void BeginTransaction ()
 
void RollbackTransaction ()
 
void CommitTransaction ()
 
int ExecuteTransaction (std::shared_ptr< TransactionBase > transaction)
 
std::size_t EscapeString (char *to, char const *from, std::size_t length)
 
void Ping ()
 
uint32 GetLastError ()
 

Protected Types

typedef std::vector< std::unique_ptr< MySQLPreparedStatement > > PreparedStatementContainer
 

Protected Member Functions

bool LockIfReady ()
 
void Unlock ()
 Called by parent databasepool. Will let other threads access this connection.
 
uint32 GetServerVersion () const
 
std::string GetServerInfo () const
 
MySQLPreparedStatement * GetPreparedStatement (uint32 index)
 
void PrepareStatement (uint32 index, std::string_view sql, ConnectionFlags flags)
 
virtual void DoPrepareStatements ()=0
 
virtual bool _HandleMySQLErrno (uint32 errNo, char const *err="", uint8 attempts=5)
 

Protected Attributes

PreparedStatementContainer m_stmts
 
bool m_reconnecting
 PreparedStatements storage.
 
bool m_prepareError
 Are we reconnecting?
 
bool m_inTransaction
 Was there any error while preparing statements?
 
uint32 m_lostTransactionError
 Is a transaction open on this connection?
 
MySQLHandle * m_Mysql
 Error that lost the open transaction's connection, 0 if none.
 

Private Member Functions

int ExecuteTransactionAttempt (std::shared_ptr< TransactionBase > const &transaction, bool &lostBeforeCommit)
 MySQL Handle.
 
int AbortTransaction (std::size_t queryCount, bool &lostBeforeCommit)
 
 MySQLConnection (MySQLConnection const &right)=delete
 
MySQLConnection & operator= (MySQLConnection const &right)=delete
 

Private Attributes

ProducerConsumerQueue< SQLOperation * > * m_queue
 
std::unique_ptr< DatabaseWorker > m_worker
 Queue shared with other asynchronous connections.
 
MySQLConnectionInfo & m_connectionInfo
 Core worker task.
 
ConnectionFlags m_connectionFlags
 Connection info (used for logging)
 
std::mutex m_Mutex
 Connection flags (for preparing relevant statements)
 

Friends

template<class T >
class DatabaseWorkerPool
 
class ModuleDatabasePool
 
class PingOperation
 

Detailed Description

Member Typedef Documentation

◆ PreparedStatementContainer

typedef std::vector<std::unique_ptr<MySQLPreparedStatement> > MySQLConnection::PreparedStatementContainer
protected

Constructor & Destructor Documentation

◆ MySQLConnection() [1/3]

MySQLConnection::MySQLConnection ( MySQLConnectionInfo &  connInfo)
51 :
52 m_reconnecting(false),
53 m_prepareError(false),
54 m_inTransaction(false),
56 m_Mysql(nullptr),
57 m_queue(nullptr),
58 m_connectionInfo(connInfo),
@ CONNECTION_SYNCH
Definition MySQLConnection.h:38
ProducerConsumerQueue< SQLOperation * > * m_queue
Definition MySQLConnection.h:117
MySQLConnectionInfo & m_connectionInfo
Core worker task.
Definition MySQLConnection.h:119
ConnectionFlags m_connectionFlags
Connection info (used for logging)
Definition MySQLConnection.h:120
bool m_prepareError
Are we reconnecting?
Definition MySQLConnection.h:108
MySQLHandle * m_Mysql
Error that lost the open transaction's connection, 0 if none.
Definition MySQLConnection.h:111
uint32 m_lostTransactionError
Is a transaction open on this connection?
Definition MySQLConnection.h:110
bool m_inTransaction
Was there any error while preparing statements?
Definition MySQLConnection.h:109
bool m_reconnecting
PreparedStatements storage.
Definition MySQLConnection.h:107

◆ MySQLConnection() [2/3]

MySQLConnection::MySQLConnection ( ProducerConsumerQueue< SQLOperation * > *  queue,
MySQLConnectionInfo &  connInfo 
)

Constructor for synchronous connections.

61 :
62 m_reconnecting(false),
63 m_prepareError(false),
64 m_inTransaction(false),
66 m_Mysql(nullptr),
67 m_queue(queue),
68 m_connectionInfo(connInfo),
70{
71 m_worker = std::make_unique<DatabaseWorker>(m_queue, this);
72}
@ CONNECTION_ASYNC
Definition MySQLConnection.h:37
std::unique_ptr< DatabaseWorker > m_worker
Queue shared with other asynchronous connections.
Definition MySQLConnection.h:118

References m_queue, and m_worker.

◆ ~MySQLConnection()

MySQLConnection::~MySQLConnection ( )
virtual

Constructor for asynchronous connections.

75{
76 Close();
77}
void Close()
Definition MySQLConnection.cpp:79

References Close().

◆ MySQLConnection() [3/3]

MySQLConnection::MySQLConnection ( MySQLConnection const &  right)
privatedelete

Member Function Documentation

◆ _HandleMySQLErrno()

bool MySQLConnection::_HandleMySQLErrno ( uint32  errNo,
char const *  err = "",
uint8  attempts = 5 
)
protectedvirtual
618{
619 std::string str = "";
620 switch (errNo)
621 {
622 case CR_SERVER_GONE_ERROR:
623 case CR_SERVER_LOST:
624 case CR_SERVER_LOST_EXTENDED:
625 {
626 if (m_Mysql)
627 {
628 LOG_ERROR("sql.sql", "Lost the connection to the MySQL server!");
629
630 mysql_close(m_Mysql);
631 m_Mysql = nullptr;
632 }
633 [[fallthrough]];
634 }
635 case CR_CONN_HOST_ERROR:
636 {
637 LOG_INFO("sql.sql", "Attempting to reconnect to the MySQL server...");
638
639 m_reconnecting = true;
640
641 uint32 const lErrno = Open();
642 if (!lErrno)
643 {
644 // Don't remove 'this' pointer unless you want to skip loading all prepared statements...
645 if (!this->PrepareStatements())
646 {
647 str = "Could not re-prepare statements!";
648 LOG_FATAL("sql.sql", "{}", str);
649 std::this_thread::sleep_for(10s);
650 ABORT("{}\n\n[{}] {}", str, errNo, err);
651 }
652
653 LOG_INFO("sql.sql", "Successfully reconnected to {} @{}:{} ({}).",
655 (m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous");
656
657 m_reconnecting = false;
658
659 // The server rolled back the open transaction, so its next statements must not run in autocommit
660 if (m_inTransaction)
661 {
662 m_inTransaction = false;
664 return false;
665 }
666
667 return true;
668 }
669
670 if ((--attempts) == 0)
671 {
672 // Shut down the server when the mysql server isn't
673 // reachable for some time
674 str = "Failed to reconnect to the MySQL server, terminating the server to prevent data corruption!";
675 LOG_FATAL("sql.sql", "{}", str);
676
677 // We could also initiate a shutdown through using std::raise(SIGTERM)
678 std::this_thread::sleep_for(10s);
679 ABORT("{}\n\n[{}] {}", str, errNo, err);
680 }
681 else
682 {
683 // It's possible this attempted reconnect throws 2006 at us.
684 // To prevent crazy recursive calls, sleep here.
685 std::this_thread::sleep_for(3s); // Sleep 3 seconds
686 return _HandleMySQLErrno(lErrno, mysql_error(m_Mysql), attempts); // Call self (recursive)
687 }
688 [[fallthrough]];
689 }
690
691 case ER_LOCK_DEADLOCK:
692 return false; // Implemented in TransactionTask::Execute and DatabaseWorkerPool<T>::DirectCommitTransaction
693
694 // Query related errors - skip query
695 case ER_WRONG_VALUE_COUNT:
696 case ER_DUP_ENTRY:
697 return false;
698
699 // Outdated table or database structure - terminate core
700 case ER_BAD_FIELD_ERROR:
701 case ER_NO_SUCH_TABLE:
702 str = "Your database structure is not up to date. Please make sure you've executed all queries in the sql/updates folders.";
703 LOG_FATAL("sql.sql", "{}", str);
704 std::this_thread::sleep_for(10s);
705 ABORT("{}\n\n[{}] {}", str, errNo, err);
706 return false;
707 case ER_PARSE_ERROR:
708 str = "Error while parsing SQL. Core fix required.";
709 LOG_FATAL("sql.sql", "{}", str);
710 std::this_thread::sleep_for(10s);
711 ABORT("{}\n\n[{}] {}", str, errNo, err);
712 return false;
713 default:
714 LOG_ERROR("sql.sql", "Unhandled MySQL errno {}. Unexpected behaviour possible.", errNo);
715 return false;
716 }
717}
std::uint32_t uint32
Definition Define.h:107
#define ABORT
Definition Errors.h:76
#define LOG_FATAL(filterType__,...)
Definition Log.h:141
#define LOG_INFO(filterType__,...)
Definition Log.h:153
#define LOG_ERROR(filterType__,...)
Definition Log.h:145
virtual bool _HandleMySQLErrno(uint32 errNo, char const *err="", uint8 attempts=5)
Definition MySQLConnection.cpp:617
virtual uint32 Open()
Definition MySQLConnection.cpp:92
bool PrepareStatements()
Definition MySQLConnection.cpp:175
std::string host
Definition MySQLConnection.h:49
std::string port_or_socket
Definition MySQLConnection.h:50
std::string database
Definition MySQLConnection.h:48

References _HandleMySQLErrno(), ABORT, CONNECTION_ASYNC, MySQLConnectionInfo::database, MySQLConnectionInfo::host, LOG_ERROR, LOG_FATAL, LOG_INFO, m_connectionFlags, m_connectionInfo, m_inTransaction, m_lostTransactionError, m_Mysql, m_reconnecting, Open(), MySQLConnectionInfo::port_or_socket, and PrepareStatements().

Referenced by _HandleMySQLErrno(), _Query(), _Query(), Execute(), and Execute().

◆ _Query() [1/2]

bool MySQLConnection::_Query ( PreparedStatementBase *  stmt,
MySQLPreparedStatement **  mysqlStmt,
MySQLResult **  pResult,
uint64 *  pRowCount,
uint32 *  pFieldCount 
)
268{
269 if (!m_Mysql)
270 return false;
271
272 uint32 index = stmt->GetIndex();
273
275 ASSERT(m_mStmt); // Can only be null if preparation failed, server side error or bad query
276
277 m_mStmt->BindParameters(stmt);
278 *mysqlStmt = m_mStmt;
279
280 MYSQL_STMT* msql_STMT = m_mStmt->GetSTMT();
281 MYSQL_BIND* msql_BIND = m_mStmt->GetBind();
282
283 uint32 _s = getMSTime();
284
285#if MYSQL_VERSION_ID >= 80300
286 if (mysql_stmt_bind_named_param(msql_STMT, msql_BIND, m_mStmt->GetParameterCount(), nullptr))
287#else
288 if (mysql_stmt_bind_param(msql_STMT, msql_BIND))
289#endif
290 {
291 uint32 lErrno = mysql_errno(m_Mysql);
292 LOG_ERROR("sql.sql", "SQL(p): {}\n [ERROR]: [{}] {}", m_mStmt->getQueryString(), lErrno, mysql_stmt_error(msql_STMT));
293
294 if (_HandleMySQLErrno(lErrno, mysql_stmt_error(msql_STMT))) // If it returns true, an error was handled successfully (i.e. reconnection)
295 return _Query(stmt, mysqlStmt, pResult, pRowCount, pFieldCount); // Try again
296
297 m_mStmt->ClearParameters();
298 return false;
299 }
300
301 if (mysql_stmt_execute(msql_STMT))
302 {
303 uint32 lErrno = mysql_errno(m_Mysql);
304 LOG_ERROR("sql.sql", "SQL(p): {}\n [ERROR]: [{}] {}", m_mStmt->getQueryString(), lErrno, mysql_stmt_error(msql_STMT));
305
306 if (_HandleMySQLErrno(lErrno, mysql_stmt_error(msql_STMT))) // If it returns true, an error was handled successfully (i.e. reconnection)
307 return _Query(stmt, mysqlStmt, pResult, pRowCount, pFieldCount); // Try again
308
309 m_mStmt->ClearParameters();
310 return false;
311 }
312
313 LOG_DEBUG("sql.sql", "[{} ms] SQL(p): {}", getMSTimeDiff(_s, getMSTime()), m_mStmt->getQueryString());
314
315 m_mStmt->ClearParameters();
316
317 *pResult = reinterpret_cast<MySQLResult*>(mysql_stmt_result_metadata(msql_STMT));
318 *pRowCount = mysql_stmt_affected_rows(msql_STMT);
319 *pFieldCount = mysql_stmt_field_count(msql_STMT);
320
321 return true;
322}
#define ASSERT
Definition Errors.h:68
#define LOG_DEBUG(filterType__,...)
Definition Log.h:157
uint32 getMSTime()
Definition Timer.h:103
uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime)
Definition Timer.h:110
MySQLPreparedStatement * GetPreparedStatement(uint32 index)
Definition MySQLConnection.cpp:553
bool _Query(std::string_view sql, MySQLResult **pResult, MySQLField **pFields, uint64 *pRowCount, uint32 *pFieldCount)
Definition MySQLConnection.cpp:340
Definition MySQLPreparedStatement.h:34
uint32 GetParameterCount() const
Definition MySQLPreparedStatement.h:44
std::string getQueryString() const
Definition MySQLPreparedStatement.cpp:189
void ClearParameters()
Definition MySQLPreparedStatement.cpp:89
MySQLStmt * GetSTMT()
Definition MySQLPreparedStatement.h:55
void BindParameters(PreparedStatementBase *stmt)
Definition MySQLPreparedStatement.cpp:68
MySQLBind * GetBind()
Definition MySQLPreparedStatement.h:56
uint32 GetIndex() const
Definition PreparedStatement.h:123
Definition MySQLHacks.h:25

References _HandleMySQLErrno(), _Query(), ASSERT, MySQLPreparedStatement::BindParameters(), MySQLPreparedStatement::ClearParameters(), MySQLPreparedStatement::GetBind(), PreparedStatementBase::GetIndex(), getMSTime(), getMSTimeDiff(), MySQLPreparedStatement::GetParameterCount(), GetPreparedStatement(), MySQLPreparedStatement::getQueryString(), MySQLPreparedStatement::GetSTMT(), LOG_DEBUG, LOG_ERROR, and m_Mysql.

◆ _Query() [2/2]

bool MySQLConnection::_Query ( std::string_view  sql,
MySQLResult **  pResult,
MySQLField **  pFields,
uint64 *  pRowCount,
uint32 *  pFieldCount 
)
341{
342 if (!m_Mysql)
343 return false;
344
345 {
346 uint32 _s = getMSTime();
347
348 if (mysql_query(m_Mysql, std::string(sql).c_str()))
349 {
350 uint32 lErrno = mysql_errno(m_Mysql);
351 LOG_INFO("sql.sql", "SQL: {}", sql);
352 LOG_ERROR("sql.sql", "[{}] {}", lErrno, mysql_error(m_Mysql));
353
354 if (_HandleMySQLErrno(lErrno, mysql_error(m_Mysql))) // If it returns true, an error was handled successfully (i.e. reconnection)
355 return _Query(sql, pResult, pFields, pRowCount, pFieldCount); // We try again
356
357 return false;
358 }
359 else
360 LOG_DEBUG("sql.sql", "[{} ms] SQL: {}", getMSTimeDiff(_s, getMSTime()), sql);
361
362 *pResult = reinterpret_cast<MySQLResult*>(mysql_store_result(m_Mysql));
363 *pRowCount = mysql_affected_rows(m_Mysql);
364 *pFieldCount = mysql_field_count(m_Mysql);
365 }
366
367 if (!*pResult)
368 return false;
369
370 if (!*pRowCount)
371 {
372 mysql_free_result(*pResult);
373 return false;
374 }
375
376 *pFields = reinterpret_cast<MySQLField*>(mysql_fetch_fields(*pResult));
377
378 return true;
379}
Definition MySQLHacks.h:26

References _HandleMySQLErrno(), _Query(), getMSTime(), getMSTimeDiff(), LOG_DEBUG, LOG_ERROR, LOG_INFO, and m_Mysql.

Referenced by _Query(), _Query(), Query(), and Query().

◆ AbortTransaction()

int MySQLConnection::AbortTransaction ( std::size_t  queryCount,
bool &  lostBeforeCommit 
)
private
497{
498 // The server already rolled the transaction back when the connection was lost
500 {
501 lostBeforeCommit = true;
502 return std::exchange(m_lostTransactionError, 0);
503 }
504
505 LOG_WARN("sql.sql", "Transaction aborted. {} queries not executed.", queryCount);
506 int errorCode = GetLastError();
507 m_inTransaction = false;
509 return errorCode;
510}
#define LOG_WARN(filterType__,...)
Definition Log.h:149
void RollbackTransaction()
Definition MySQLConnection.cpp:386
uint32 GetLastError()
Definition MySQLConnection.cpp:528

References GetLastError(), LOG_WARN, m_inTransaction, m_lostTransactionError, and RollbackTransaction().

Referenced by ExecuteTransactionAttempt().

◆ BeginTransaction()

void MySQLConnection::BeginTransaction ( )
382{
383 Execute("START TRANSACTION");
384}
bool Execute(std::string_view sql)
Definition MySQLConnection.cpp:181

References Execute().

Referenced by ExecuteTransactionAttempt().

◆ Close()

void MySQLConnection::Close ( )
80{
81 // Stop the worker thread before the statements are cleared
82 m_worker.reset();
83 m_stmts.clear();
84
85 if (m_Mysql)
86 {
87 mysql_close(m_Mysql);
88 m_Mysql = nullptr;
89 }
90}
PreparedStatementContainer m_stmts
Definition MySQLConnection.h:106

References m_Mysql, m_stmts, and m_worker.

Referenced by ~MySQLConnection().

◆ CommitTransaction()

void MySQLConnection::CommitTransaction ( )
392{
393 Execute("COMMIT");
394}

References Execute().

Referenced by ExecuteTransactionAttempt().

◆ DoPrepareStatements()

virtual void MySQLConnection::DoPrepareStatements ( )
protectedpure virtual

◆ EscapeString()

std::size_t MySQLConnection::EscapeString ( char *  to,
char const *  from,
std::size_t  length 
)
513{
514 return mysql_real_escape_string(m_Mysql, to, from, length);
515}

References m_Mysql.

◆ Execute() [1/2]

bool MySQLConnection::Execute ( PreparedStatementBase *  stmt)
209{
210 if (!m_Mysql)
211 return false;
212
213 uint32 index = stmt->GetIndex();
214
216 ASSERT(m_mStmt); // Can only be null if preparation failed, server side error or bad query
217
218 m_mStmt->BindParameters(stmt);
219
220 MYSQL_STMT* msql_STMT = m_mStmt->GetSTMT();
221 MYSQL_BIND* msql_BIND = m_mStmt->GetBind();
222
223 uint32 _s = getMSTime();
224
225#if MYSQL_VERSION_ID >= 80300
226 if (mysql_stmt_bind_named_param(msql_STMT, msql_BIND, m_mStmt->GetParameterCount(), nullptr))
227#else
228 if (mysql_stmt_bind_param(msql_STMT, msql_BIND))
229#endif
230 {
231 uint32 lErrno = mysql_errno(m_Mysql);
232 LOG_ERROR("sql.sql", "SQL(p): {}\n [ERROR]: [{}] {}", m_mStmt->getQueryString(), lErrno, mysql_stmt_error(msql_STMT));
233
234 if (_HandleMySQLErrno(lErrno, mysql_stmt_error(msql_STMT))) // If it returns true, an error was handled successfully (i.e. reconnection)
235 return Execute(stmt); // Try again
236
237 // Reconnected inside a transaction: m_mStmt was replaced by the new connection's statements
239 return false;
240
241 m_mStmt->ClearParameters();
242 return false;
243 }
244
245 if (mysql_stmt_execute(msql_STMT))
246 {
247 uint32 lErrno = mysql_errno(m_Mysql);
248 LOG_ERROR("sql.sql", "SQL(p): {}\n [ERROR]: [{}] {}", m_mStmt->getQueryString(), lErrno, mysql_stmt_error(msql_STMT));
249
250 if (_HandleMySQLErrno(lErrno, mysql_stmt_error(msql_STMT))) // If it returns true, an error was handled successfully (i.e. reconnection)
251 return Execute(stmt); // Try again
252
253 // Reconnected inside a transaction: m_mStmt was replaced by the new connection's statements
255 return false;
256
257 m_mStmt->ClearParameters();
258 return false;
259 }
260
261 LOG_DEBUG("sql.sql", "[{} ms] SQL(p): {}", getMSTimeDiff(_s, getMSTime()), m_mStmt->getQueryString());
262
263 m_mStmt->ClearParameters();
264 return true;
265}

References _HandleMySQLErrno(), ASSERT, MySQLPreparedStatement::BindParameters(), MySQLPreparedStatement::ClearParameters(), Execute(), MySQLPreparedStatement::GetBind(), PreparedStatementBase::GetIndex(), getMSTime(), getMSTimeDiff(), MySQLPreparedStatement::GetParameterCount(), GetPreparedStatement(), MySQLPreparedStatement::getQueryString(), MySQLPreparedStatement::GetSTMT(), LOG_DEBUG, LOG_ERROR, m_lostTransactionError, and m_Mysql.

◆ Execute() [2/2]

bool MySQLConnection::Execute ( std::string_view  sql)
182{
183 if (!m_Mysql)
184 return false;
185
186 {
187 uint32 _s = getMSTime();
188
189 if (mysql_query(m_Mysql, std::string(sql).c_str()))
190 {
191 uint32 lErrno = mysql_errno(m_Mysql);
192
193 LOG_INFO("sql.sql", "SQL: {}", sql);
194 LOG_ERROR("sql.sql", "[{}] {}", lErrno, mysql_error(m_Mysql));
195
196 if (_HandleMySQLErrno(lErrno, mysql_error(m_Mysql))) // If it returns true, an error was handled successfully (i.e. reconnection)
197 return Execute(sql); // Try again
198
199 return false;
200 }
201 else
202 LOG_DEBUG("sql.sql", "[{} ms] SQL: {}", getMSTimeDiff(_s, getMSTime()), sql);
203 }
204
205 return true;
206}

References _HandleMySQLErrno(), Execute(), getMSTime(), getMSTimeDiff(), LOG_DEBUG, LOG_ERROR, LOG_INFO, and m_Mysql.

Referenced by BeginTransaction(), CommitTransaction(), ModuleDatabasePool::DirectExecute(), ModuleDatabasePool::DirectExecute(), BasicStatementTask::Execute(), PreparedStatementTask::Execute(), Execute(), Execute(), ExecuteTransactionAttempt(), and RollbackTransaction().

◆ ExecuteTransaction()

int MySQLConnection::ExecuteTransaction ( std::shared_ptr< TransactionBase >  transaction)
397{
398 if (transaction->m_queries.empty())
399 return -1;
400
401 // The server rolls back a transaction whose connection is lost, so one lost before COMMIT can run again
402 static constexpr uint8 MAX_TRANSACTION_ATTEMPTS = 3;
403
404 for (uint8 attempt = 1; ; ++attempt)
405 {
406 bool lostBeforeCommit = false;
407 int errorCode = ExecuteTransactionAttempt(transaction, lostBeforeCommit);
408
409 if (!lostBeforeCommit)
410 return errorCode;
411
412 if (attempt == MAX_TRANSACTION_ATTEMPTS)
413 {
414 LOG_ERROR("sql.sql", "Transaction lost its connection {} times. {} queries not executed.",
415 attempt, transaction->m_queries.size());
416 return errorCode;
417 }
418
419 LOG_WARN("sql.sql", "Transaction lost its connection before COMMIT, running it again.");
420 }
421}
std::uint8_t uint8
Definition Define.h:109
int ExecuteTransactionAttempt(std::shared_ptr< TransactionBase > const &transaction, bool &lostBeforeCommit)
MySQL Handle.
Definition MySQLConnection.cpp:423

References ExecuteTransactionAttempt(), LOG_ERROR, and LOG_WARN.

Referenced by ModuleDatabasePool::TryDirectCommitTransaction(), and TransactionTask::TryExecute().

◆ ExecuteTransactionAttempt()

int MySQLConnection::ExecuteTransactionAttempt ( std::shared_ptr< TransactionBase > const &  transaction,
bool &  lostBeforeCommit 
)
private

MySQL Handle.

425{
426 std::vector<SQLElementData> const& queries = transaction->m_queries;
427
429 m_inTransaction = true;
430
431 for (auto const& data : queries)
432 {
433 switch (data.type)
434 {
436 {
437 PreparedStatementBase* stmt = nullptr;
438
439 try
440 {
441 stmt = std::get<PreparedStatementBase*>(data.element);
442 }
443 catch (std::bad_variant_access const& ex)
444 {
445 LOG_FATAL("sql.sql", "> PreparedStatementBase not found in SQLElementData. {}", ex.what());
446 ABORT();
447 }
448
449 ASSERT(stmt);
450
451 if (!Execute(stmt))
452 return AbortTransaction(queries.size(), lostBeforeCommit);
453 }
454 break;
455 case SQL_ELEMENT_RAW:
456 {
457 std::string sql{};
458
459 try
460 {
461 sql = std::get<std::string>(data.element);
462 }
463 catch (std::bad_variant_access const& ex)
464 {
465 LOG_FATAL("sql.sql", "> std::string not found in SQLElementData. {}", ex.what());
466 ABORT();
467 }
468
469 ASSERT(!sql.empty());
470
471 if (!Execute(sql))
472 return AbortTransaction(queries.size(), lostBeforeCommit);
473 }
474 break;
475 }
476 }
477
478 // we might encounter errors during certain queries, and depending on the kind of error
479 // we might want to restart the transaction. So to prevent data loss, we only clean up when it's all done.
480 // This is done in calling functions DatabaseWorkerPool<T>::DirectCommitTransaction and TransactionTask::Execute,
481 // and not while iterating over every element.
482
484 m_inTransaction = false;
485
487 {
488 LOG_ERROR("sql.sql", "Lost the connection during COMMIT. The transaction may or may not have been written, "
489 "so it is not run again.");
490 return std::exchange(m_lostTransactionError, 0);
491 }
492
493 return 0;
494}
@ SQL_ELEMENT_RAW
Definition SQLOperation.h:28
@ SQL_ELEMENT_PREPARED
Definition SQLOperation.h:29
int AbortTransaction(std::size_t queryCount, bool &lostBeforeCommit)
Definition MySQLConnection.cpp:496
void CommitTransaction()
Definition MySQLConnection.cpp:391
void BeginTransaction()
Definition MySQLConnection.cpp:381
Definition PreparedStatement.h:68

References ABORT, AbortTransaction(), ASSERT, BeginTransaction(), CommitTransaction(), Execute(), LOG_ERROR, LOG_FATAL, m_inTransaction, m_lostTransactionError, SQL_ELEMENT_PREPARED, and SQL_ELEMENT_RAW.

Referenced by ExecuteTransaction().

◆ GetLastError()

uint32 MySQLConnection::GetLastError ( )
529{
530 return mysql_errno(m_Mysql);
531}

References m_Mysql.

Referenced by AbortTransaction().

◆ GetPreparedStatement()

MySQLPreparedStatement * MySQLConnection::GetPreparedStatement ( uint32  index)
protected
554{
555 ASSERT(index < m_stmts.size(), "Tried to access invalid prepared statement index {} (max index {}) on database `{}`, connection type: {}",
556 index, m_stmts.size(), m_connectionInfo.database, (m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous");
557
558 MySQLPreparedStatement* ret = m_stmts[index].get();
559
560 if (!ret)
561 LOG_ERROR("sql.sql", "Could not fetch prepared statement {} on database `{}`, connection type: {}.",
562 index, m_connectionInfo.database, (m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous");
563
564 return ret;
565}

References ASSERT, CONNECTION_ASYNC, MySQLConnectionInfo::database, LOG_ERROR, m_connectionFlags, m_connectionInfo, and m_stmts.

Referenced by _Query(), and Execute().

◆ GetServerInfo()

std::string MySQLConnection::GetServerInfo ( ) const
protected
549{
550 return mysql_get_server_info(m_Mysql);
551}

References m_Mysql.

◆ GetServerVersion()

uint32 MySQLConnection::GetServerVersion ( ) const
protected
544{
545 return mysql_get_server_version(m_Mysql);
546}

References m_Mysql.

◆ LockIfReady()

bool MySQLConnection::LockIfReady ( )
protected

Tries to acquire lock. If lock is acquired by another thread the calling parent will just try another connection

534{
535 return m_Mutex.try_lock();
536}
std::mutex m_Mutex
Connection flags (for preparing relevant statements)
Definition MySQLConnection.h:121

References m_Mutex.

Referenced by ModuleDatabasePool::GetFreeConnection().

◆ Open()

uint32 MySQLConnection::Open ( )
virtual
93{
94 MYSQL* mysqlInit = mysql_init(nullptr);
95 if (!mysqlInit)
96 {
97 LOG_ERROR("sql.driver", "Could not initialize Mysql connection to database `{}`", m_connectionInfo.database);
98 return CR_UNKNOWN_ERROR;
99 }
100
101 uint32 port;
102 char const* unix_socket;
103 char const* host = m_connectionInfo.host.c_str();
104
105 mysql_options(mysqlInit, MYSQL_SET_CHARSET_NAME, "utf8");
106
107#ifdef _WIN32
108 if (m_connectionInfo.host == ".") // named pipe use option (Windows)
109 {
110 unsigned int opt = MYSQL_PROTOCOL_PIPE;
111 mysql_options(mysqlInit, MYSQL_OPT_PROTOCOL, (char const*)&opt);
112 port = 0;
113 unix_socket = 0;
114 }
115 else // generic case
116 {
117 port = *Acore::StringTo<uint32>(m_connectionInfo.port_or_socket);
118 unix_socket = 0;
119 }
120#else
121 if (m_connectionInfo.host == ".") // socket use option (Unix/Linux)
122 {
123 unsigned int opt = MYSQL_PROTOCOL_SOCKET;
124 mysql_options(mysqlInit, MYSQL_OPT_PROTOCOL, (char const*)&opt);
125 host = "localhost";
126 port = 0;
127 unix_socket = m_connectionInfo.port_or_socket.c_str();
128 }
129 else // generic case
130 {
131 port = *Acore::StringTo<uint32>(m_connectionInfo.port_or_socket);
132 unix_socket = nullptr;
133 }
134#endif
135
136 if (m_connectionInfo.ssl != "")
137 {
138 mysql_ssl_mode opt_use_ssl = SSL_MODE_DISABLED;
139 if (m_connectionInfo.ssl == "ssl")
140 {
141 opt_use_ssl = SSL_MODE_REQUIRED;
142 }
143
144 mysql_options(mysqlInit, MYSQL_OPT_SSL_MODE, (char const*)&opt_use_ssl);
145 }
146
147 m_Mysql = reinterpret_cast<MySQLHandle*>(mysql_real_connect(mysqlInit, host, m_connectionInfo.user.c_str(),
148 m_connectionInfo.password.c_str(), m_connectionInfo.database.c_str(), port, unix_socket, 0));
149
150 if (m_Mysql)
151 {
152 if (!m_reconnecting)
153 {
154 LOG_INFO("sql.sql", "MySQL client library: {}", mysql_get_client_info());
155 LOG_INFO("sql.sql", "MySQL server ver: {} ", mysql_get_server_info(m_Mysql));
156 }
157
158 LOG_INFO("sql.sql", "Connected to MySQL database at {}", host);
159 mysql_autocommit(m_Mysql, 1);
160
161 // set connection properties to UTF8 to properly handle locales for different
162 // server configs - core sends data in UTF8, so MySQL must expect UTF8 too
163 mysql_set_character_set(m_Mysql, "utf8mb4");
164 return 0;
165 }
166 else
167 {
168 LOG_ERROR("sql.driver", "Could not connect to MySQL database at {}: {}", host, mysql_error(mysqlInit));
169 uint32 errorCode = mysql_errno(mysqlInit);
170 mysql_close(mysqlInit);
171 return errorCode;
172 }
173}
std::string user
Definition MySQLConnection.h:46
std::string ssl
Definition MySQLConnection.h:51
std::string password
Definition MySQLConnection.h:47
Definition MySQLHacks.h:24

References MySQLConnectionInfo::database, MySQLConnectionInfo::host, LOG_ERROR, LOG_INFO, m_connectionInfo, m_Mysql, m_reconnecting, MySQLConnectionInfo::password, MySQLConnectionInfo::port_or_socket, MySQLConnectionInfo::ssl, and MySQLConnectionInfo::user.

Referenced by _HandleMySQLErrno().

◆ operator=()

MySQLConnection & MySQLConnection::operator= ( MySQLConnection const &  right)
privatedelete

◆ Ping()

void MySQLConnection::Ping ( )
518{
519 mysql_ping(m_Mysql);
520}

References m_Mysql.

Referenced by PingOperation::Execute().

◆ PrepareStatement()

void MySQLConnection::PrepareStatement ( uint32  index,
std::string_view  sql,
ConnectionFlags  flags 
)
protected
568{
569 // Check if specified query should be prepared on this connection
570 // i.e. don't prepare async statements on synchronous connections
571 // to save memory that will not be used.
572 if (!(m_connectionFlags & flags))
573 {
574 m_stmts[index].reset();
575 return;
576 }
577
578 MYSQL_STMT* stmt = mysql_stmt_init(m_Mysql);
579 if (!stmt)
580 {
581 LOG_ERROR("sql.sql", "In mysql_stmt_init() id: {}, sql: \"{}\"", index, sql);
582 LOG_ERROR("sql.sql", "{}", mysql_error(m_Mysql));
583 m_prepareError = true;
584 }
585 else
586 {
587 if (mysql_stmt_prepare(stmt, std::string(sql).c_str(), static_cast<unsigned long>(sql.size())))
588 {
589 LOG_ERROR("sql.sql", "In mysql_stmt_prepare() id: {}, sql: \"{}\"", index, sql);
590 LOG_ERROR("sql.sql", "{}", mysql_stmt_error(stmt));
591 mysql_stmt_close(stmt);
592 m_prepareError = true;
593 }
594 else
595 m_stmts[index] = std::make_unique<MySQLPreparedStatement>(reinterpret_cast<MySQLStmt*>(stmt), sql);
596 }
597}
Definition MySQLHacks.h:28

References LOG_ERROR, m_connectionFlags, m_Mysql, m_prepareError, and m_stmts.

Referenced by CharacterDatabaseConnection::DoPrepareStatements(), LoginDatabaseConnection::DoPrepareStatements(), and WorldDatabaseConnection::DoPrepareStatements().

◆ PrepareStatements()

bool MySQLConnection::PrepareStatements ( )
176{
178 return !m_prepareError;
179}
virtual void DoPrepareStatements()=0

References DoPrepareStatements(), and m_prepareError.

Referenced by _HandleMySQLErrno().

◆ Query() [1/2]

PreparedResultSet * MySQLConnection::Query ( PreparedStatementBase *  stmt)
600{
601 MySQLPreparedStatement* mysqlStmt = nullptr;
602 MySQLResult* result = nullptr;
603 uint64 rowCount = 0;
604 uint32 fieldCount = 0;
605
606 if (!_Query(stmt, &mysqlStmt, &result, &rowCount, &fieldCount))
607 return nullptr;
608
609 if (mysql_more_results(m_Mysql))
610 {
611 mysql_next_result(m_Mysql);
612 }
613
614 return new PreparedResultSet(mysqlStmt->GetSTMT(), result, rowCount, fieldCount);
615}
std::uint64_t uint64
Definition Define.h:106
Definition QueryResult.h:99

References _Query(), MySQLPreparedStatement::GetSTMT(), and m_Mysql.

◆ Query() [2/2]

ResultSet * MySQLConnection::Query ( std::string_view  sql)
325{
326 if (sql.empty())
327 return nullptr;
328
329 MySQLResult* result = nullptr;
330 MySQLField* fields = nullptr;
331 uint64 rowCount = 0;
332 uint32 fieldCount = 0;
333
334 if (!_Query(sql, &result, &fields, &rowCount, &fieldCount))
335 return nullptr;
336
337 return new ResultSet(result, fields, rowCount, fieldCount);
338}
Definition QueryResult.h:49

References _Query().

Referenced by BasicStatementTask::Execute(), PreparedStatementTask::Execute(), SQLQueryHolderTask::Execute(), ModuleDatabasePool::Query(), and ModuleDatabasePool::Query().

◆ RollbackTransaction()

void MySQLConnection::RollbackTransaction ( )
387{
388 Execute("ROLLBACK");
389}

References Execute().

Referenced by AbortTransaction().

◆ Unlock()

void MySQLConnection::Unlock ( )
protected

Friends And Related Symbol Documentation

◆ DatabaseWorkerPool

template<class T >
friend class DatabaseWorkerPool
friend

◆ ModuleDatabasePool

friend class ModuleDatabasePool
friend

◆ PingOperation

friend class PingOperation
friend

Member Data Documentation

◆ m_connectionFlags

ConnectionFlags MySQLConnection::m_connectionFlags
private

Connection info (used for logging)

Referenced by _HandleMySQLErrno(), GetPreparedStatement(), and PrepareStatement().

◆ m_connectionInfo

MySQLConnectionInfo& MySQLConnection::m_connectionInfo
private

Core worker task.

Referenced by _HandleMySQLErrno(), GetPreparedStatement(), and Open().

◆ m_inTransaction

bool MySQLConnection::m_inTransaction
protected

Was there any error while preparing statements?

Referenced by _HandleMySQLErrno(), AbortTransaction(), and ExecuteTransactionAttempt().

◆ m_lostTransactionError

uint32 MySQLConnection::m_lostTransactionError
protected

Is a transaction open on this connection?

Referenced by _HandleMySQLErrno(), AbortTransaction(), Execute(), and ExecuteTransactionAttempt().

◆ m_Mutex

std::mutex MySQLConnection::m_Mutex
private

Connection flags (for preparing relevant statements)

Referenced by LockIfReady(), and Unlock().

◆ m_Mysql

MySQLHandle* MySQLConnection::m_Mysql
protected

Error that lost the open transaction's connection, 0 if none.

Referenced by _HandleMySQLErrno(), _Query(), _Query(), Close(), EscapeString(), Execute(), Execute(), GetLastError(), GetServerInfo(), GetServerVersion(), Open(), Ping(), PrepareStatement(), and Query().

◆ m_prepareError

bool MySQLConnection::m_prepareError
protected

Are we reconnecting?

Referenced by PrepareStatement(), and PrepareStatements().

◆ m_queue

ProducerConsumerQueue<SQLOperation*>* MySQLConnection::m_queue
private

Referenced by MySQLConnection().

◆ m_reconnecting

◆ m_stmts

◆ m_worker

std::unique_ptr<DatabaseWorker> MySQLConnection::m_worker
private

Queue shared with other asynchronous connections.

Referenced by Close(), and MySQLConnection().


The documentation for this class was generated from the following files: