// 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 .
#ifndef MYSQL_WRAPPER_H
#define MYSQL_WRAPPER_H
#include "nel/misc/types_nl.h"
#include
#include "nel/misc/debug.h"
#include "nel/misc/common.h"
#include "nel/misc/md5.h"
#include "nel/net/service.h"
#include "nel/net/module_common.h"
#include "game_share/utils.h"
#ifdef NL_OS_WINDOWS
# include
# include
typedef unsigned long ulong;
#endif
#include
#include
#include "game_share/r2_basic_types.h" // for TSessionId
namespace MSW
{
class CStoreResult;
class CUseResult;
class CConnection;
/// Utility function to encode a date
std::string encodeDate(NLMISC::TTime date);
/** escape string using 'mysql_real_escape_string' and return a the escaped string.
* Note that the function is internally optimized to quickly build the return string
* with very little overhead.
*/
const std::string &escapeString(const std::string &str, CConnection &dbCnx);
class CConnection
{
friend const std::string &escapeString(const std::string &str, CConnection &dbCnx);
/// Connection info
std::string _ConnHostName;
std::string _ConnUserName;
std::string _ConnPassword;
std::string _ConnDefaultDatabase;
/// The mysql connection context
MYSQL *_MysqlContext;
/// Flag for connection open
bool _Connected;
typedef std::map TOptions;
/// A list of pair name/value of connection option
TOptions _Options;
/// Internale connect method (this one do the job)
bool _connect();
public:
CConnection()
: _MysqlContext(NULL),
_Connected(false)
{
}
~CConnection()
{
if (_Connected)
mysql_close(_MysqlContext);
}
void addOption(mysql_option option, const char *value);
void clearOption(mysql_option option);
bool connect(const std::string &hostName, const std::string &userName, const std::string &password, const std::string &defaultDatabase);
bool connect(const NLNET::TParsedCommandLine &databaseInfo);
void closeConn();
bool query(const std::string &queryString);
uint32 getLastGeneratedId()
{
return uint32(mysql_insert_id(_MysqlContext));
}
uint32 getAffectedRows()
{
return uint32(mysql_affected_rows(_MysqlContext));
}
std::auto_ptr storeResult();
std::auto_ptr useResult();
};
// base class contains method to extract info from a fetched row
class CResultBase
{
protected:
MYSQL_ROW _CurrentRow;
unsigned long *_FieldLength;
MYSQL_RES *_Result;
CResultBase(MYSQL_RES *result)
: _CurrentRow(NULL),
_FieldLength(NULL),
_Result(result)
{
}
public:
~CResultBase()
{
mysql_free_result(_Result);
}
/// return the number of fields in the result set
uint32 getNumFields()
{
return mysql_num_fields(_Result);
}
const char *getRawField(uint32 fieldIndex)
{
nlassert(_CurrentRow != NULL);
nlassert(fieldIndex < getNumFields());
static const char *emptyString = "";
char *ret = _CurrentRow[fieldIndex];
return ret != NULL ? ret : emptyString;
}
void getField(uint32 fieldIndex, std::string &value)
{
value = getRawField(fieldIndex);
}
void getField(uint32 fieldIndex, bool &value)
{
const char *str = getRawField(fieldIndex);
if (str[0] == '1')
value = true;
else if (str[0] == '0')
value = false;
else
value = NLMISC::nlstricmp(str, "true") == 0;
}
void getField(uint32 fieldIndex, uint8 &value)
{
const char *str = getRawField(fieldIndex);
value = uint8(strtoul(str, NULL, 10));
}
void getField(uint32 fieldIndex, uint32 &value)
{
const char *str = getRawField(fieldIndex);
value = uint32(strtoul(str, NULL, 10));
}
void getField(uint32 fieldIndex, sint8 &value)
{
NLMISC::fromString(std::string(getRawField(fieldIndex)), value);
}
void getField(uint32 fieldIndex, sint32 &value)
{
NLMISC::fromString(std::string(getRawField(fieldIndex)), value);
}
void getField(uint32 fieldIndex, TSessionId &value)
{
sint32 val;
NLMISC::fromString(std::string(getRawField(fieldIndex)), val);
value =TSessionId(val);
}
void getField(uint32 fieldIndex, std::vector &value)
{
const char *data = getRawField(fieldIndex);
long size = _FieldLength[fieldIndex];
std::vector vec(data, data+size);
value.swap(vec);
}
void getMD5Field(uint32 fieldIndex, NLMISC::CHashKeyMD5 &hashKey)
{
const char *data = getRawField(fieldIndex);
long size = _FieldLength[fieldIndex];
hashKey.fromString(data);
}
void getDateField(uint32 fieldIndex, uint32 &time);
};
class CUseResult : public CResultBase
{
friend class CConnection;
CUseResult(MYSQL_RES *result)
: CResultBase(result)
{
}
public:
/// Advance to next row in the result set, return true is there is a row
bool fetchRow()
{
_CurrentRow = mysql_fetch_row(_Result);
if (_CurrentRow == NULL)
return false;
_FieldLength = mysql_fetch_lengths(_Result);
nlassert(_FieldLength != NULL);
return true;
}
};
class CStoreResult : public CResultBase
{
friend class CConnection;
CStoreResult(MYSQL_RES *result)
: CResultBase(result)
{
}
public:
/// Return the number of row in the result set
uint32 getNumRows()
{
return uint32(mysql_num_rows(_Result));
}
/// Advance to next row in the result set
void fetchRow()
{
_CurrentRow = mysql_fetch_row(_Result);
nlassert(_CurrentRow != NULL);
_FieldLength = mysql_fetch_lengths(_Result);
nlassert(_FieldLength != NULL);
}
};
} // namespace MSW
//NeL simple Object Persistence Engine
namespace NOPE
{
enum TObjectState
{
/// This is a temporary allocated, not persisted object instance
os_transient,
/// This is a persistent object, unmodified since it was loaded
os_clean,
/// This is a persistent object, modified in transient space that need to be saved to db
os_dirty,
/// This is removed object, it should no more be used as it have been removed from db
os_removed,
/// This is a persistent object, unmodified but that lie in the object cache.
os_released,
// a tag counter MUST BE LAST
os_nb_state,
};
const uint32 INVALID_OBJECT_ID = 0;
extern bool AllowedTransition [os_nb_state][os_nb_state];
enum TCacheCmd
{
cc_update,
cc_clear,
cc_dump,
cc_instance_count,
};
typedef uint32 (*TCacheCmdFunc)(TCacheCmd cmd);
class CPersistentCache :
public NLNET::IServiceUpdatable,
public NLMISC::ICommandsHandler
{
NLMISC_SAFE_SINGLETON_DECL(CPersistentCache);
typedef std::set TUpdateFuncs;
TUpdateFuncs _UpdateFuncs;
CPersistentCache()
{
NLMISC::CCommandRegistry::getInstance().registerNamedCommandHandler(this, "CPersistentCache");
}
// virtual const std::string &getCommandHandlerClassName() const
// {
// static string name("CPersistentCache");
// return name;
// }
virtual const std::string &getCommandHandlerName() const
{
static std::string name("sqlObjectCache");
return name;
}
public:
void registerCache(TCacheCmdFunc functionPtr)
{
_UpdateFuncs.insert(functionPtr);
}
void serviceLoopUpdate()
{
H_AUTO(CPersistentCache_serviceLoopUpdate);
TUpdateFuncs::iterator first(_UpdateFuncs.begin()), last(_UpdateFuncs.end());
for (; first != last; ++first)
{
TCacheCmdFunc f = *first;
f(cc_update);
}
}
// delete any unreference object in the cache
void clearCache()
{
TUpdateFuncs::iterator first(_UpdateFuncs.begin()), last(_UpdateFuncs.end());
for (; first != last; ++first)
{
TCacheCmdFunc f = *first;
f(cc_clear);
}
}
/// Return the grand total of instance in memory (including in use and in cache objects)
uint32 getInstanceCount()
{
uint32 total = 0;
TUpdateFuncs::iterator first(_UpdateFuncs.begin()), last(_UpdateFuncs.end());
for (; first != last; ++first)
{
TCacheCmdFunc f = *first;
total += f(cc_instance_count);
}
return total;
}
NLMISC_COMMAND_HANDLER_TABLE_BEGIN(CPersistentCache)
NLMISC_COMMAND_HANDLER_ADD(CPersistentCache, clearCache, "remove any unreferenced cached object from memory", "no params")
NLMISC_COMMAND_HANDLER_ADD(CPersistentCache, dump, "dump cache status", "no params")
NLMISC_COMMAND_HANDLER_TABLE_END
NLMISC_CLASS_COMMAND_DECL(dump)
{
log.displayNL("Dumping SQL cache for %u class :", _UpdateFuncs.size());
// redirect all logs to log
NLMISC::CNLSmartLogOverride logRedirector(&log);
TUpdateFuncs::iterator first(_UpdateFuncs.begin()), last(_UpdateFuncs.end());
for (; first != last; ++first)
{
TCacheCmdFunc f = *first;
f(cc_dump);
}
return true;
}
NLMISC_CLASS_COMMAND_DECL(clearCache)
{
// hop
clearCache();
return true;
}
};
} // namespace NOPE
#endif // MYSQL_WRAPPER_H