// Ryzom - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "stdpch.h"
#include
#include "mysql_wrapper.h"
#include
using namespace std;
using namespace NLMISC;
using namespace NLNET;
CVariable MSWStrictMode("msw", "MSWStrictMode", "Set the strict mode on SQL request", true, 0, true);
CVariable MSWRequestDuration("msw", "MSWRequestDuration", "Measure the duration of SQL request", 0, 1000);
CVariable MSWAutoReconnect("msw", "MSWAutoReconnect", "MYSQL_OPT_RECONNECT", true, 0, true);
namespace MSW
{
std::string encodeDate(NLMISC::TTime date)
{
time_t baseTime = time_t(date);
// convert UTC time_t into local time, including daillight adjustment
struct tm *converted = localtime(&baseTime);
if (converted == NULL)
{
nldebug("Failed to convert %dl into a time structure", date);
return "0000-00-00 00:00:00";
}
std::string str;
str = NLMISC::toString("%4u-%02u-%02u %u:%02u:%02u",
converted->tm_year+1900,
converted->tm_mon+1,
converted->tm_mday,
converted->tm_hour,
converted->tm_min,
converted->tm_sec);
return str;
}
const std::string &escapeString(const std::string &str, CConnection &dbCnx)
{
static string buffer;
// reserve space in the buffer according to the mysql documentation
buffer.resize(str.size()*2+1);
unsigned long resultSize = mysql_escape_string((char*)buffer.data(), str.data(), (unsigned long)str.size());
// now, resize to the real size
buffer.resize(resultSize);
// job's done
return buffer;
}
void CConnection::addOption(mysql_option option, const char *value)
{
_Options[option] = value;
}
void CConnection::clearOption(mysql_option option)
{
_Options.erase(option);
}
bool CConnection::connect(const TParsedCommandLine &dbInfo)
{
const TParsedCommandLine *dbHost= dbInfo.getParam("host");
const TParsedCommandLine *dbport= dbInfo.getParam("port");
const TParsedCommandLine *dbUser= dbInfo.getParam("user");
const TParsedCommandLine *dbPassword= dbInfo.getParam("password");
const TParsedCommandLine *dbBase= dbInfo.getParam("base");
if (dbHost == NULL
|| dbUser == NULL
|| dbPassword == NULL
|| dbBase == NULL)
{
nlwarning("LS : Invalid database configuration");
if (MSWStrictMode)
{
nlstopex(("SQL Strict mode, the above error is fatal"));
}
return false;
}
// connect to the database
return connect(dbHost->ParamValue, dbUser->ParamValue, dbPassword->ParamValue, dbBase->ParamValue);
}
bool CConnection::connect(const std::string &hostName, const std::string &userName, const std::string &password, const std::string &defaultDatabase)
{
// store the connection info
_ConnHostName = hostName;
_ConnUserName = userName;
_ConnPassword = password;
_ConnDefaultDatabase = defaultDatabase;
nlassert(!_Connected);
if (MSWAutoReconnect)
{
addOption(MYSQL_OPT_RECONNECT, "1");
}
return _connect();
}
bool CConnection::_connect()
{
_Connected = false;
if (_MysqlContext != NULL)
{
mysql_close(_MysqlContext);
}
// init the context
_MysqlContext = mysql_init(NULL);
// set the options
TOptions::iterator first(_Options.begin()), last(_Options.end());
for (; first != last; ++first)
{
const mysql_option &option = first->first;
const char *value = first->second;
mysql_options(_MysqlContext, option, value);
}
MYSQL *res = mysql_real_connect(_MysqlContext,
_ConnHostName.c_str(),
_ConnUserName.c_str(),
_ConnPassword.c_str(),
_ConnDefaultDatabase.c_str(),
0,
NULL,
CLIENT_FOUND_ROWS);
if (res == NULL)
{
nlwarning("Error during connection to database '%s:%s@%s' :", _ConnUserName.c_str(), _ConnDefaultDatabase.c_str(), _ConnHostName.c_str());
nlwarning("Mysql_real_connect error :%s ", mysql_error(_MysqlContext));
// the connection failed !
mysql_close(_MysqlContext);
_MysqlContext = NULL;
if (MSWStrictMode)
{
nlstopex(("SQL Strict mode, the above error is fatal"));
}
return false;
}
_Connected = true;
return true;
}
void CConnection::closeConn()
{
nlassert(_Connected);
// the connection failed !
mysql_close(_MysqlContext);
_Connected = 0;
_MysqlContext = NULL;
}
bool CConnection::query(const std::string &queryString)
{
H_AUTO(CConnection_query);
nlassert(_Connected);
nldebug("Executing query : [%s%s", queryString.substr(0, 100).c_str(), 100queryString.size() ? "]" : "");
TTime startDate = CTime::getLocalTime();
int result = mysql_real_query(_MysqlContext, queryString.c_str(), (unsigned long)queryString.size());
if (result != 0)
{
// in all case, we try to reconnect
int merrno = mysql_errno(_MysqlContext);
//if (result == CR_SERVER_GONE_ERROR)
{
// reconnect and retry the request
nlinfo("%p Mysql error errno:%d result:%d : %s, try to reconnect...", _MysqlContext, merrno, result, mysql_error(_MysqlContext));
if (_connect())
result = mysql_real_query(_MysqlContext, queryString.c_str(), (unsigned long)queryString.size());
else
{
nlwarning("Failed to reopen closed connection to send query '%s'", queryString.c_str());
if (MSWStrictMode)
{
nlstopex(("SQL Strict mode, the above error is fatal"));
}
TTime endDate = CTime::getLocalTime();
MSWRequestDuration = uint32(endDate - startDate);
return false;
}
}
if (result != 0)
{
nlwarning("Mysql error errno:%d result:%d : %s", merrno, result, mysql_error(_MysqlContext));
nlwarning(" in query '%s':", queryString.c_str());
if (MSWStrictMode)
{
nlstopex(("SQL Strict mode, the above error is fatal"));
}
TTime endDate = CTime::getLocalTime();
MSWRequestDuration = uint32(endDate - startDate);
return false;
}
}
TTime endDate = CTime::getLocalTime();
MSWRequestDuration = uint32(endDate - startDate);
return true;
}
std::auto_ptr CConnection::storeResult()
{
H_AUTO(CConnection_storeResult);
MYSQL_RES *res = mysql_store_result(_MysqlContext);
std::auto_ptr sr = std::auto_ptr(new CStoreResult(res));
return sr;
}
std::auto_ptr CConnection::useResult()
{
H_AUTO(CConnection_useResult);
MYSQL_RES *res = mysql_use_result(_MysqlContext);
std::auto_ptr sr = std::auto_ptr(new CUseResult(res));
return sr;
}
void CResultBase::getDateField(uint32 fieldIndex, uint32 &time)
{
const char *str = getRawField(fieldIndex);
// read from a DATE_TIME field
MYSQL_FIELD *field = mysql_fetch_field_direct(_Result, fieldIndex);
nlassert(field != NULL);
switch (field->type)
{
case FIELD_TYPE_DATETIME:
{
// format is 'YYYY-MM-DD HH:MM:SS'
uint y, m, d, h, mn, s;
uint nbScanned = sscanf(str, "%u-%u-%u %u:%u:%u", &y, &m, &d, &h, &mn, &s);
nlassert(nbScanned == 6);
tm myTm;
myTm.tm_year = y-1900;
myTm.tm_mon = m-1;
myTm.tm_mday = d;
myTm.tm_hour = h;
myTm.tm_min = mn;
myTm.tm_sec = s;
myTm.tm_isdst = -1; // let the C runtime determine daylight adjustment
myTm.tm_wday = -1;
myTm.tm_yday = -1;
// Convert the local date into a UTC unix time
uint32 t = (uint32)nl_mktime(&myTm);
time = t;
}
break;
case FIELD_TYPE_DATE:
{
// format is 'YYYY-MM-DD'
uint y, m, d;
uint nbScanned = sscanf(str, "%u-%u-%u", &y, &m, &d);
nlassert(nbScanned == 3);
tm myTm;
myTm.tm_year = y-1900;
myTm.tm_mon = m-1;
myTm.tm_mday = d;
myTm.tm_hour = 0;
myTm.tm_min = 0;
myTm.tm_sec = 0;
myTm.tm_isdst = -1; // let the C runtime determine daylight adjustment
myTm.tm_wday = -1;
myTm.tm_yday = -1;
// Convert the local date into a UTC unix time
uint32 t = (uint32)nl_mktime(&myTm);
time = t;
}
break;
case FIELD_TYPE_TIME:
{
// format is 'HH:MM:SS'
uint h, m, s;
uint nbScanned = sscanf(str, "%u:%u:%u", &h, &m, &s);
nlassert(nbScanned == 3);
tm myTm;
myTm.tm_year = 0;
myTm.tm_mon = 0;
myTm.tm_mday = 0;
myTm.tm_hour = h;
myTm.tm_min = m;
myTm.tm_sec = s;
myTm.tm_isdst = -1; // let the C runtime determine daylight adjustment
myTm.tm_wday = -1;
myTm.tm_yday = -1;
// Convert the local date into a UTC unix time
uint32 t = (uint32)nl_mktime(&myTm);
time = t;
}
break;
case FIELD_TYPE_YEAR:
{
// format is 'YYYY'
uint y;
uint nbScanned = sscanf(str, "%u", &y);
nlassert(nbScanned == 1);
tm myTm;
myTm.tm_year = y-1900;
myTm.tm_mon = 0;
myTm.tm_mday = 0;
myTm.tm_hour = 0;
myTm.tm_min = 0;
myTm.tm_sec = 0;
myTm.tm_isdst = -1; // let the C runtime determine daylight adjustment
myTm.tm_wday = -1;
myTm.tm_yday = -1;
// Convert the local date into a UTC unix time
uint32 t = (uint32)nl_mktime(&myTm);
time = t;
}
break;
default:
{
// any other case, consider the field as a second unix time (seconds since 1970)
// format is 'SSSSSSSSSS..'
uint32 t;
getField(fieldIndex, t);
time = t;
}
}
}
} // namespace MSW
namespace NOPE
{
// allowed transition table
bool AllowedTransition [os_nb_state][os_nb_state] =
{
// transient clean dirty removed released
/*transient*/ { true, true, true, false, false },
/*clean*/ { false, true, true, true, true },
/*dirty*/ { false, true, true, true, false },
/*removed*/ { false, false, false, false, false },
/*released*/ { false, false, false, false, false },
};
NLMISC_SAFE_SINGLETON_IMPL(CPersistentCache);
NLMISC_CATEGORISED_DYNVARIABLE("nope", bool, NOPEAllowReleaseDirtyObject, "Set to true to allow dirty object to be removed from cache (this mean some modified data are lost)")
{
if (get)
*pointer = AllowedTransition[os_dirty][os_released];
else
AllowedTransition[os_dirty][os_released] = (*pointer);
}
} // namespace NOPE