zaela-Server/common/dbcore.cpp

266 lines
5.8 KiB
C++
Raw Permalink Normal View History

2013-02-16 16:14:39 -08:00
#ifdef _WINDOWS
#include <winsock2.h>
#endif
#include "misc_functions.h"
#include "eqemu_logsys.h"
#include "timer.h"
2014-12-15 20:04:49 -06:00
#include "dbcore.h"
#include <errmsg.h>
#include <fstream>
2013-02-16 16:14:39 -08:00
#include <iostream>
#include <mysqld_error.h>
#include <string.h>
#ifdef _WINDOWS
#define snprintf _snprintf
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#include <process.h>
2013-02-16 16:14:39 -08:00
#else
#include "unix.h"
#include <pthread.h>
2013-02-16 16:14:39 -08:00
#endif
#ifdef _EQDEBUG
#define DEBUG_MYSQL_QUERIES 0
2013-02-16 16:14:39 -08:00
#else
#define DEBUG_MYSQL_QUERIES 0
2013-02-16 16:14:39 -08:00
#endif
DBcore::DBcore()
{
2013-02-16 16:14:39 -08:00
mysql_init(&mysql);
pHost = 0;
pUser = 0;
2013-02-16 16:14:39 -08:00
pPassword = 0;
pDatabase = 0;
pCompress = false;
pSSL = false;
pStatus = Closed;
2013-02-16 16:14:39 -08:00
}
DBcore::~DBcore()
{
2013-02-16 16:14:39 -08:00
mysql_close(&mysql);
safe_delete_array(pHost);
safe_delete_array(pUser);
safe_delete_array(pPassword);
safe_delete_array(pDatabase);
}
// Sends the MySQL server a keepalive
void DBcore::ping()
{
2013-02-16 16:14:39 -08:00
if (!MDatabase.trylock()) {
// well, if's it's locked, someone's using it. If someone's using it, it doesnt need a keepalive
return;
}
mysql_ping(&mysql);
MDatabase.unlock();
}
2014-07-06 16:15:54 -07:00
MySQLRequestResult DBcore::QueryDatabase(std::string query, bool retryOnFailureOnce)
{
return QueryDatabase(query.c_str(), query.length(), retryOnFailureOnce);
}
MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, bool retryOnFailureOnce)
{
2019-07-06 03:33:41 -05:00
BenchTimer timer;
timer.reset();
LockMutex lock(&MDatabase);
// Reconnect if we are not connected before hand.
if (pStatus != Connected) {
Open();
}
// request query. != 0 indicates some kind of error.
if (mysql_real_query(&mysql, query, querylen) != 0) {
unsigned int errorNumber = mysql_errno(&mysql);
if (errorNumber == CR_SERVER_GONE_ERROR) {
pStatus = Error;
}
// error appears to be a disconnect error, may need to try again.
if (errorNumber == CR_SERVER_LOST || errorNumber == CR_SERVER_GONE_ERROR) {
if (retryOnFailureOnce) {
2019-09-01 23:22:40 -05:00
LogInfo("Database Error: Lost connection, attempting to recover");
MySQLRequestResult requestResult = QueryDatabase(query, querylen, false);
if (requestResult.Success()) {
2019-09-01 23:22:40 -05:00
LogInfo("Reconnection to database successful");
return requestResult;
}
}
pStatus = Error;
2016-05-25 16:10:28 -04:00
auto errorBuffer = new char[MYSQL_ERRMSG_SIZE];
snprintf(errorBuffer, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql));
return MySQLRequestResult(nullptr, 0, 0, 0, 0, (uint32) mysql_errno(&mysql), errorBuffer);
}
2016-05-25 16:10:28 -04:00
auto errorBuffer = new char[MYSQL_ERRMSG_SIZE];
snprintf(errorBuffer, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql));
2020-02-05 01:56:39 -06:00
/**
* Error logging
*/
if (mysql_errno(&mysql) > 0 && strlen(query) > 0) {
2020-02-05 01:56:39 -06:00
LogMySQLError("[{}] [{}]\n[{}]", mysql_errno(&mysql), mysql_error(&mysql), query);
}
return MySQLRequestResult(nullptr, 0, 0, 0, 0, mysql_errno(&mysql), errorBuffer);
}
// successful query. get results.
MYSQL_RES *res = mysql_store_result(&mysql);
uint32 rowCount = 0;
if (res != nullptr) {
rowCount = (uint32) mysql_num_rows(res);
}
MySQLRequestResult requestResult(
res,
(uint32) mysql_affected_rows(&mysql),
rowCount,
(uint32) mysql_field_count(&mysql),
2019-07-05 18:34:08 -05:00
(uint32) mysql_insert_id(&mysql)
);
if (LogSys.log_settings[Logs::MySQLQuery].is_category_enabled == 1) {
if ((strncasecmp(query, "select", 6) == 0)) {
2020-02-05 01:56:39 -06:00
LogMySQLQuery(
"{0} ({1} row{2} returned) ({3}s)",
query,
requestResult.RowCount(),
2019-07-05 18:34:08 -05:00
requestResult.RowCount() == 1 ? "" : "s",
std::to_string(timer.elapsed())
);
}
else {
2020-02-05 01:56:39 -06:00
LogMySQLQuery(
"{0} ({1} row{2} affected) ({3}s)",
query,
requestResult.RowsAffected(),
2019-07-05 18:34:08 -05:00
requestResult.RowsAffected() == 1 ? "" : "s",
std::to_string(timer.elapsed())
);
}
}
return requestResult;
}
void DBcore::TransactionBegin()
{
QueryDatabase("START TRANSACTION");
}
void DBcore::TransactionCommit()
{
QueryDatabase("COMMIT");
}
void DBcore::TransactionRollback()
{
QueryDatabase("ROLLBACK");
}
uint32 DBcore::DoEscapeString(char *tobuf, const char *frombuf, uint32 fromlen)
{
2013-02-16 16:14:39 -08:00
// No good reason to lock the DB, we only need it in the first place to check char encoding.
// LockMutex lock(&MDatabase);
return mysql_real_escape_string(&mysql, tobuf, frombuf, fromlen);
}
bool DBcore::Open(
const char *iHost,
const char *iUser,
const char *iPassword,
const char *iDatabase,
uint32 iPort,
uint32 *errnum,
char *errbuf,
bool iCompress,
bool iSSL
)
{
2013-02-16 16:14:39 -08:00
LockMutex lock(&MDatabase);
safe_delete(pHost);
safe_delete(pUser);
safe_delete(pPassword);
safe_delete(pDatabase);
pHost = strcpy(new char[strlen(iHost) + 1], iHost);
pUser = strcpy(new char[strlen(iUser) + 1], iUser);
2013-02-16 16:14:39 -08:00
pPassword = strcpy(new char[strlen(iPassword) + 1], iPassword);
pDatabase = strcpy(new char[strlen(iDatabase) + 1], iDatabase);
pCompress = iCompress;
pPort = iPort;
pSSL = iSSL;
2013-02-16 16:14:39 -08:00
return Open(errnum, errbuf);
}
bool DBcore::Open(uint32 *errnum, char *errbuf)
{
if (errbuf) {
2013-02-16 16:14:39 -08:00
errbuf[0] = 0;
}
2013-02-16 16:14:39 -08:00
LockMutex lock(&MDatabase);
if (GetStatus() == Connected) {
2013-02-16 16:14:39 -08:00
return true;
}
2013-03-08 18:07:28 -08:00
if (GetStatus() == Error) {
2013-02-16 16:14:39 -08:00
mysql_close(&mysql);
mysql_init(&mysql); // Initialize structure again
2013-05-09 10:44:08 -04:00
}
if (!pHost) {
2013-02-16 16:14:39 -08:00
return false;
}
2013-02-16 16:14:39 -08:00
/*
Added CLIENT_FOUND_ROWS flag to the connect
otherwise DB update calls would say 0 rows affected when the value already equalled
2013-05-06 13:07:41 -04:00
what the function was tring to set it to, therefore the function would think it failed
2013-02-16 16:14:39 -08:00
*/
uint32 flags = CLIENT_FOUND_ROWS;
if (pCompress) {
2013-02-16 16:14:39 -08:00
flags |= CLIENT_COMPRESS;
}
if (pSSL) {
2013-02-16 16:14:39 -08:00
flags |= CLIENT_SSL;
}
2013-02-16 16:14:39 -08:00
if (mysql_real_connect(&mysql, pHost, pUser, pPassword, pDatabase, pPort, 0, flags)) {
pStatus = Connected;
return true;
}
else {
if (errnum) {
2013-02-16 16:14:39 -08:00
*errnum = mysql_errno(&mysql);
}
if (errbuf) {
2013-02-16 16:14:39 -08:00
snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql));
}
2013-02-16 16:14:39 -08:00
pStatus = Error;
return false;
}
}