Merge branch 'sql'

This commit is contained in:
Eduardo Bart 2013-03-06 13:00:21 -03:00
commit 489688e9fb
10 changed files with 558 additions and 217 deletions

View File

@ -3,10 +3,12 @@ project(otclient)
set(VERSION "0.6.2") set(VERSION "0.6.2")
set(FRAMEWORK_SOUND ON) option(FRAMEWORK_SOUND "Use SOUND " ON)
set(FRAMEWORK_GRAPHICS ON) option(FRAMEWORK_GRAPHICS "Use GRAPHICS " ON)
set(FRAMEWORK_XML ON) option(FRAMEWORK_XML "Use XML " ON)
set(FRAMEWORK_NET ON) option(FRAMEWORK_NET "Use NET " ON)
option(FRAMEWORK_SQL "Use SQL" OFF)
include(src/framework/CMakeLists.txt) include(src/framework/CMakeLists.txt)
include(src/client/CMakeLists.txt) include(src/client/CMakeLists.txt)

View File

@ -3,6 +3,7 @@
# FRAMEWORK_GRAPHICS # FRAMEWORK_GRAPHICS
# FRAMEWORK_NET # FRAMEWORK_NET
# FRAMEWORK_XML # FRAMEWORK_XML
# FRAMEWORK_SQL
# CMAKE_CURRENT_LIST_DIR cmake 2.6 compatibility # CMAKE_CURRENT_LIST_DIR cmake 2.6 compatibility
if(${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 6) if(${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 6)
@ -233,11 +234,10 @@ set(framework_LIBRARIES ${framework_LIBRARIES}
set(framework_INCLUDE_DIRS ${framework_INCLUDE_DIRS} set(framework_INCLUDE_DIRS ${framework_INCLUDE_DIRS}
${Boost_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}
${OPENGL_INCLUDE_DIR}
${LUA_INCLUDE_DIR} ${LUA_INCLUDE_DIR}
${PHYSFS_INCLUDE_DIR} ${PHYSFS_INCLUDE_DIR}
${OpenSSL_INCLUDE_DIR} ${OpenSSL_INCLUDE_DIR}
${ZLIB_INCLUDE_DIR} ${framework_INCLUDE_DIRS}
) )
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
@ -279,10 +279,10 @@ else()
else() else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -rdynamic -Wl,-rpath,./libs") # rdynamic is needed by backtrace.h used in crash handler set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -rdynamic -Wl,-rpath,./libs") # rdynamic is needed by backtrace.h used in crash handler
set(SYSTEM_LIBRARIES dl) set(SYSTEM_LIBRARIES dl rt)
endif() endif()
set(framework_LIBRARIES ${framework_LIBRARIES} ${SYSTEM_LIBRARIES})
endif() endif()
set(framework_LIBRARIES ${framework_LIBRARIES} ${SYSTEM_LIBRARIES})
if(FRAMEWORK_THREAD_SAFE) if(FRAMEWORK_THREAD_SAFE)
set(framework_DEFINITIONS ${framework_DEFINITIONS} -DTHREAD_SAFE) set(framework_DEFINITIONS ${framework_DEFINITIONS} -DTHREAD_SAFE)
@ -462,14 +462,12 @@ if(FRAMEWORK_SOUND)
find_package(Ogg REQUIRED) find_package(Ogg REQUIRED)
set(framework_INCLUDE_DIRS ${framework_INCLUDE_DIRS} ${OPENAL_INCLUDE_DIR} ${VORBISFILE_INCLUDE_DIR}) set(framework_INCLUDE_DIRS ${framework_INCLUDE_DIRS} ${OPENAL_INCLUDE_DIR} ${VORBISFILE_INCLUDE_DIR})
set(framework_LIBRARIES ${framework_LIBRARIES} ${OPENAL_LIBRARY} ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY} ${OGG_LIBRARY}) set(framework_LIBRARIES ${OPENAL_LIBRARY} ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY} ${OGG_LIBRARY} ${framework_LIBRARIES})
if(WIN32) if(WIN32)
set(framework_LIBRARIES ${framework_LIBRARIES} winmm) set(framework_LIBRARIES ${framework_LIBRARIES} winmm)
elseif(APPLE) elseif(APPLE)
set(framework_LIBRARIES ${framework_LIBRARIES} System) set(framework_LIBRARIES ${framework_LIBRARIES} System)
else()
set(framework_LIBRARIES ${framework_LIBRARIES} rt)
endif() endif()
set(framework_SOURCES ${framework_SOURCES} set(framework_SOURCES ${framework_SOURCES}
@ -536,7 +534,7 @@ if(FRAMEWORK_SQL)
find_package(MySQL REQUIRED) find_package(MySQL REQUIRED)
set(framework_INCLUDE_DIRS ${framework_INCLUDE_DIRS} ${MYSQL_INCLUDE_DIR}) set(framework_INCLUDE_DIRS ${framework_INCLUDE_DIRS} ${MYSQL_INCLUDE_DIR})
set(framework_LIBRARIES ${framework_LIBRARIES} ${MYSQL_LIBRARY}) set(framework_LIBRARIES ${MYSQL_LIBRARY} ${framework_LIBRARIES})
set(framework_SOURCES ${framework_SOURCES} set(framework_SOURCES ${framework_SOURCES}
${CMAKE_CURRENT_LIST_DIR}/sql/declarations.h ${CMAKE_CURRENT_LIST_DIR}/sql/declarations.h

View File

@ -4,8 +4,14 @@
# MYSQL_LIBRARY - the mysql library # MYSQL_LIBRARY - the mysql library
FIND_PATH(MYSQL_INCLUDE_DIR NAMES mysql.h PATH_SUFFIXES mysql) FIND_PATH(MYSQL_INCLUDE_DIR NAMES mysql.h PATH_SUFFIXES mysql)
SET(_MYSQL_STATIC_LIBS libmysqlclient.a libmysqlclient_r.a) IF(WIN32)
SET(_MYSQL_SHARED_LIBS libmysqlclient.dll.a libmysqlclient_r.dll.a mysqlclient mysqlclient_r) SET(_MYSQL_STATIC_LIBS libmysql.a libmysql_r.a)
SET(_MYSQL_SHARED_LIBS libmysql.dll.a libmysql_r.dll.a libmysql libmysql_r)
ELSE()
SET(_MYSQL_STATIC_LIBS libmysqlclient.a libmysqlclient_r.a)
SET(_MYSQL_SHARED_LIBS libmysqlclient.dll.a libmysqlclient_r.dll.a mysqlclient mysqlclient_r)
ENDIF()
IF(USE_STATIC_LIBS) IF(USE_STATIC_LIBS)
FIND_LIBRARY(MYSQL_LIBRARY NAMES ${_MYSQL_STATIC_LIBS} ${_MYSQL_SHARED_LIBS}) FIND_LIBRARY(MYSQL_LIBRARY NAMES ${_MYSQL_STATIC_LIBS} ${_MYSQL_SHARED_LIBS})
ELSE() ELSE()
@ -14,3 +20,4 @@ ENDIF()
INCLUDE(FindPackageHandleStandardArgs) INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(MySQL DEFAULT_MSG MYSQL_LIBRARY MYSQL_INCLUDE_DIR) FIND_PACKAGE_HANDLE_STANDARD_ARGS(MySQL DEFAULT_MSG MYSQL_LIBRARY MYSQL_INCLUDE_DIR)
MARK_AS_ADVANCED(MYSQL_LIBRARY MYSQL_INCLUDE_DIR) MARK_AS_ADVANCED(MYSQL_LIBRARY MYSQL_INCLUDE_DIR)

View File

@ -288,6 +288,13 @@ namespace Fw
BackgroundPane = 2, BackgroundPane = 2,
BothPanes = 3 BothPanes = 3
}; };
#ifdef FW_SQL
enum DatabaseEngine {
DatabaseNone = 0,
DatabaseMySQL
};
#endif
} }
#endif #endif

View File

@ -840,22 +840,50 @@ void Application::registerLuaFunctions()
#ifdef FW_SQL #ifdef FW_SQL
// Database // Database
g_lua.registerClass<Database>(); g_lua.registerClass<Database>();
g_lua.bindClassMemberFunction<Database>("getDatabaseEngine", &Database::getDatabaseEngine);
g_lua.bindClassMemberFunction<Database>("isConnected", &Database::isConnected);
g_lua.bindClassMemberFunction<Database>("getStringComparer", &Database::getStringComparer);
g_lua.bindClassMemberFunction<Database>("getUpdateLimiter", &Database::getUpdateLimiter);
g_lua.bindClassMemberFunction<Database>("getLastInsertedRowID", &Database::getLastInsertedRowID);
g_lua.bindClassMemberFunction<Database>("escapeString", &Database::escapeString);
//g_lua.bindClassMemberFunction<Database>("escapeBlob", &Database::escapeBlob); // need to write a cast for this type to work (if needed)
// DBQuery
/* (not sure if this class will work as a luafunction)
g_lua.registerClass<DBQuery>();
g_lua.bindClassStaticFunction<DBQuery>("create", []{ return DBQuery(); });
g_lua.bindClassMemberFunction<DBQuery>("append", &DBQuery::append);
g_lua.bindClassMemberFunction<DBQuery>("set", &DBQuery::set);
*/
// DBResult // DBResult
g_lua.registerClass<DBResult>(); g_lua.registerClass<DBResult>();
g_lua.bindClassMemberFunction<DBResult>("getDataInt", &DBResult::getDataInt); g_lua.bindClassMemberFunction<DBResult>("getDataInt", &DBResult::getDataInt);
g_lua.bindClassMemberFunction<DBResult>("getDataLong", &DBResult::getDataLong); g_lua.bindClassMemberFunction<DBResult>("getDataLong", &DBResult::getDataLong);
g_lua.bindClassMemberFunction<DBResult>("getDataString", &DBResult::getDataString); g_lua.bindClassMemberFunction<DBResult>("getDataString", &DBResult::getDataString);
//g_lua.bindClassMemberFunction<DBResult>("getDataStream", &DBResult::getDataStream); // need to write a cast for this type to work (if needed)
g_lua.bindClassMemberFunction<DBResult>("getRowCount", &DBResult::getRowCount); g_lua.bindClassMemberFunction<DBResult>("getRowCount", &DBResult::getRowCount);
g_lua.bindClassMemberFunction<DBResult>("free", &DBResult::free);
g_lua.bindClassMemberFunction<DBResult>("next", &DBResult::next); g_lua.bindClassMemberFunction<DBResult>("next", &DBResult::next);
// MySQL
// Mysql
g_lua.registerClass<DatabaseMySQL, Database>(); g_lua.registerClass<DatabaseMySQL, Database>();
g_lua.bindClassStaticFunction<DatabaseMySQL>("create", []{ return DatabaseMySQLPtr(new DatabaseMySQL); }); g_lua.bindClassStaticFunction<DatabaseMySQL>("create", []{ return DatabaseMySQLPtr(new DatabaseMySQL); });
g_lua.bindClassMemberFunction<DatabaseMySQL>("connect", &DatabaseMySQL::connect); g_lua.bindClassMemberFunction<DatabaseMySQL>("connect", &DatabaseMySQL::connect);
g_lua.bindClassMemberFunction<DatabaseMySQL>("beginTransaction", &DatabaseMySQL::beginTransaction);
g_lua.bindClassMemberFunction<DatabaseMySQL>("rollback", &DatabaseMySQL::rollback);
g_lua.bindClassMemberFunction<DatabaseMySQL>("commit", &DatabaseMySQL::commit);
g_lua.bindClassMemberFunction<DatabaseMySQL>("executeQuery", &DatabaseMySQL::executeQuery); g_lua.bindClassMemberFunction<DatabaseMySQL>("executeQuery", &DatabaseMySQL::executeQuery);
g_lua.bindClassMemberFunction<DatabaseMySQL>("storeQuery", &DatabaseMySQL::storeQuery); g_lua.bindClassMemberFunction<DatabaseMySQL>("storeQuery", &DatabaseMySQL::storeQuery);
g_lua.bindClassMemberFunction<DatabaseMySQL>("escapeString", &DatabaseMySQL::escapeString);
// MySQLResult
g_lua.registerClass<MySQLResult>();
g_lua.bindClassMemberFunction<MySQLResult>("getDataInt", &MySQLResult::getDataInt);
g_lua.bindClassMemberFunction<MySQLResult>("getDataLong", &MySQLResult::getDataLong);
g_lua.bindClassMemberFunction<MySQLResult>("getDataString", &MySQLResult::getDataString);
//g_lua.bindClassMemberFunction<MySQLResult>("getDataStream", &MySQLResult::getDataStream); // need to write a cast for this type to work (if needed)
g_lua.bindClassMemberFunction<MySQLResult>("getRowCount", &MySQLResult::getRowCount);
g_lua.bindClassMemberFunction<MySQLResult>("free", &MySQLResult::free);
g_lua.bindClassMemberFunction<MySQLResult>("next", &MySQLResult::next);
#endif #endif
} }

View File

@ -22,10 +22,57 @@
#include "database.h" #include "database.h"
Database::Database() boost::recursive_mutex DBQuery::databaseLock;
DBResultPtr Database::verifyResult(DBResultPtr result)
{ {
if(result->next())
return result;
result->free();
return nullptr;
} }
Database::~Database() void DBInsert::setQuery(const std::string& query)
{ {
m_query = query;
m_buf = "";
m_rows = 0;
}
bool DBInsert::addRow(const std::string& row)
{
++m_rows;
if(m_buf.empty()) {
m_buf = "(" + row + ")";
}
else if(m_buf.length() > 8192)
{
if(!execute())
return false;
m_buf = "(" + row + ")";
}
else
m_buf += ",(" + row + ")";
return true;
}
bool DBInsert::addRow(std::stringstream& row)
{
bool ret = addRow(row.str());
row.str("");
return ret;
}
bool DBInsert::execute()
{
if(m_buf.empty() || !m_rows)
return true;
m_rows = 0;
bool ret = m_db->executeQuery(m_query + m_buf);
m_buf = "";
return ret;
} }

View File

@ -22,16 +22,286 @@
#ifndef DATABASE_H #ifndef DATABASE_H
#define DATABASE_H #define DATABASE_H
#include "declarations.h" #include "declarations.h"
#include <framework/global.h>
#include <framework/luaengine/luaobject.h> #include <framework/luaengine/luaobject.h>
#include <boost/thread.hpp>
class Database : public LuaObject class Database : public LuaObject
{ {
public: public:
Database(); friend class DBTransaction;
~Database();
Database(): m_connected(false) {}
virtual ~Database() { m_connected = false; }
/**
* Database connector.
*
* Connects the database to the source host.
*/
virtual void connect(const std::string& host, const std::string& user, const std::string& pass,
const std::string& db, uint16 port, const std::string& unix_socket = "") {}
/**
* Transaction related methods.
*
* Methods for starting, commiting and rolling back transaction. Each of the returns boolean value.
*
* @return true on success, false on error
* @note#include <ctime>
* If your database system doesn't support transactions you should return true - it's not feature test, code should work without transaction, just will lack integrity.
*/
virtual bool beginTransaction() { return false; }
virtual bool rollback() { return false; }
virtual bool commit() { return false; }
/**
* Executes command.
*
* Executes query which doesn't generates results (eg. INSERT, UPDATE, DELETE...).
*
* @param std::string query command
* @return true on success, false on error
*/
virtual bool executeQuery(const std::string& query) { return false; }
/**
* Queries database.
*
* Executes query which generates results (mostly SELECT).
*
* @param std::string query
* @return results object (null on error)
*/
virtual DBResultPtr storeQuery(const std::string& query) { return nullptr; }
/**
* Escapes string for query.
*
* Prepares string to fit SQL queries including quoting it.
*
* @param std::string string to be escaped
* @return quoted string
*/
virtual std::string escapeString(const std::string&) { return "''"; }
/**
* Escapes binary stream for query.
*
* Prepares binary stream to fit SQL queries.
*
* @param char* binary stream
* @param long stream length
* @return quoted string
*/
virtual std::string escapeBlob(const char*, uint32) { return "''"; }
/**
* Retrieve id of last inserted row
*
* @return id on success, 0 if last query did not result on any rows with auto_increment keys
*/
virtual uint64 getLastInsertedRowID() { return 0; }
/**
* Get case insensitive string comparison operator
*
* @return the case insensitive operator
*/
virtual std::string getStringComparer() { return "= "; }
virtual std::string getUpdateLimiter() { return " LIMIT 1;"; }
/**
* Get database engine
*
* @return the database engine type
*/
virtual Fw::DatabaseEngine getDatabaseEngine() { return Fw::DatabaseNone; }
/**
* Database connected.
*
* Returns whether or not the database is connected.
*
* @return whether or not the database is connected.
*/
bool isConnected() { return m_connected; }
protected:
/**
* Database set connected.
*
* Sets the database to know that it is connected.
*/
void setConnected(bool connected) { m_connected = connected; }
virtual bool handleError() { return false; }
virtual bool internalExecuteQuery(const std::string &query) { return false; }
DBResultPtr verifyResult(DBResultPtr result);
ticks_t m_use;
bool m_connected;
private:
static DatabasePtr m_instance;
};
class DBResult : public LuaObject
{
public:
DBResult() {}
virtual ~DBResult() {}
/** Get the Integer value of a field in database
*\returns The Integer value of the selected field and row
*\param s The name of the field
*/
virtual int32 getDataInt(const std::string&) { return 0; }
/** Get the Long value of a field in database
*\returns The Long value of the selected field and row
*\param s The name of the field
*/
virtual int64 getDataLong(const std::string&) { return 0; }
/** Get the String of a field in database
*\returns The String of the selected field and row
*\param s The name of the field
*/
virtual std::string getDataString(const std::string&) { return ""; }
/** Get the blob of a field in database
*\returns a PropStream that is initiated with the blob data field, if not exist it returns NULL.
*\param s The name of the field
*/
virtual const char* getDataStream(const std::string&, uint64&) { return ""; }
/** Result freeing
*/
virtual void free() {}
/** Moves to next result in set
*\returns true if moved, false if there are no more results.
*/
virtual bool next() { return false; }
/** Returned the number of rows from result
*\returns integer value of row amount, 0 if result is empty.
*/
virtual int getRowCount() { return 0; }
};
/**
* Thread locking hack.
*
* By using this class for your queries you lock and unlock database for threads.
*/
class DBQuery : public std::stringstream, public LuaObject
{
friend class Database;
public:
DBQuery() { databaseLock.lock(); }
~DBQuery() { databaseLock.unlock(); }
void set(std::string& query) { str(query); }
void append(char query) { putback(query); }
protected:
static boost::recursive_mutex databaseLock;
};
/**
* INSERT statement.
*
* Gives possibility to optimize multiple INSERTs on databases that support multiline INSERTs.
*/
class DBInsert
{
public:
/**
* Associates with given database handler.
*
* @param DatabasePtr database wrapper
*/
DBInsert(const DatabasePtr& db): m_db(db), m_rows(0) {}
~DBInsert() {}
/**
* Sets query prototype.
*
* @param std::string& INSERT query
*/
void setQuery(const std::string& query);
/**
* Adds new row to INSERT statement.
*
* On databases that doesn't support multiline INSERTs it simply execute INSERT for each row.
*
* @param std::string& row data
*/
bool addRow(const std::string& row);
/**
* Allows to use addRow() with stringstream as parameter.
*/
bool addRow(std::stringstream& row);
/**
* Executes current buffer.
*/
bool execute();
protected:
DatabasePtr m_db;
uint32 m_rows;
std::string m_query;
std::string m_buf;
};
class DBTransaction
{
public:
DBTransaction(DatabasePtr database)
{
m_db = database;
m_state = STATE_FRESH;
}
~DBTransaction()
{
if(m_state == STATE_READY)
m_db->rollback();
}
bool begin()
{
m_state = STATE_READY;
return m_db->beginTransaction();
}
bool commit()
{
if(m_state != STATE_READY)
return false;
m_state = STATE_DONE;
return m_db->commit();
}
private:
DatabasePtr m_db;
enum TransactionStates_t
{
STATE_FRESH,
STATE_READY,
STATE_DONE
} m_state;
}; };
#endif #endif

View File

@ -25,10 +25,17 @@
#include <framework/global.h> #include <framework/global.h>
class Database;
class DBResult; class DBResult;
class DatabaseMySQL;
typedef stdext::shared_object_ptr<Database> DatabasePtr;
typedef stdext::shared_object_ptr<DBResult> DBResultPtr; typedef stdext::shared_object_ptr<DBResult> DBResultPtr;
// MySQL
class DatabaseMySQL;
class MySQLResult;
typedef stdext::shared_object_ptr<DatabaseMySQL> DatabaseMySQLPtr; typedef stdext::shared_object_ptr<DatabaseMySQL> DatabaseMySQLPtr;
typedef stdext::shared_object_ptr<MySQLResult> MySQLResultPtr;
#endif #endif

View File

@ -20,46 +20,50 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#ifdef WIN32
#include <winsock2.h>
#endif
#include "mysql.h" #include "mysql.h"
#include <mysql/errmsg.h> #include <mysql/errmsg.h>
#include <framework/core/clock.h>
#include <framework/core/logger.h> #include <framework/core/logger.h>
DatabaseMySQL::DatabaseMySQL() : m_running(false) DatabaseMySQL::DatabaseMySQL()
{ {
if(!mysql_init(&m_mysqlHandle)) m_handle = new MYSQL();
if(!mysql_init(m_handle)) {
g_logger.fatal("Failed to initialize MySQL connection handle."); g_logger.fatal("Failed to initialize MySQL connection handle.");
}
my_bool reconnect = true; my_bool reconnect = true;
mysql_options(&m_mysqlHandle, MYSQL_OPT_RECONNECT, &reconnect); mysql_options(m_handle, MYSQL_OPT_RECONNECT, &reconnect);
} }
DatabaseMySQL::~DatabaseMySQL() DatabaseMySQL::~DatabaseMySQL()
{ {
mysql_close(&m_mysqlHandle); mysql_close(m_handle);
delete m_handle;
} }
void DatabaseMySQL::connect(const std::string& host, const std::string& user, const std::string& pass, void DatabaseMySQL::connect(const std::string& host, const std::string& user, const std::string& pass,
const std::string& db, uint16_t port, const std::string& unix_socket) const std::string& db, uint16 port, const std::string& unix_socket)
{ {
if(!mysql_real_connect(&m_mysqlHandle, if(!mysql_real_connect(m_handle,
host.c_str(), host.c_str(),
user.c_str(), user.c_str(),
pass.c_str(), pass.c_str(),
db.c_str(), db.c_str(),
port, port,
unix_socket.empty() ? NULL : unix_socket.c_str(), 0)) { unix_socket.empty() ? NULL : unix_socket.c_str(), 0)) {
g_logger.error(stdext::format("Failed to connect to database. MYSQL ERROR: %s", mysql_error(&m_mysqlHandle))); g_logger.error(stdext::format("Failed to connect to database. MYSQL ERROR: %s", mysql_error(m_handle)));
}
else {
setConnected(true);
} }
} }
bool DatabaseMySQL::handleError() bool DatabaseMySQL::handleError()
{ {
unsigned int error = mysql_errno(&m_mysqlHandle); unsigned int error = mysql_errno(m_handle);
g_logger.error(stdext::format("MYSQL error code = %d, message: %s", error, mysql_error(&m_mysqlHandle))); g_logger.error(stdext::format("MYSQL error code = %d, message: %s", error, mysql_error(m_handle)));
if(error == CR_SOCKET_CREATE_ERROR || if(error == CR_SOCKET_CREATE_ERROR ||
error == CR_CONNECTION_ERROR || error == CR_CONNECTION_ERROR ||
@ -70,18 +74,19 @@ bool DatabaseMySQL::handleError()
error == CR_SERVER_LOST || error == CR_SERVER_LOST ||
error == CR_SERVER_HANDSHAKE_ERR) { error == CR_SERVER_HANDSHAKE_ERR) {
g_logger.error("MYSQL connection lost, trying to reconnect..."); g_logger.error("MYSQL connection lost, trying to reconnect...");
setConnected(false);
//int64_t startTime = g_clock.millis(); ticks_t startTime = stdext::millis();
while(true) {
/*while(true) { bool connected = (mysql_ping(m_handle) == 0);
bool connected = (mysql_ping(&m_mysqlHandle) == 0); ticks_t diffTime = (stdext::millis() - startTime);
uint32_t diffTime = (mTime() - startTime);
if(connected) { if(connected) {
g_logger.info(stdext::format("MySQL reconneted in %d ms", diffTime)); g_logger.info(stdext::format("MySQL reconneted in %d ms", diffTime));
setConnected(true);
return true; return true;
} }
mSleep(100); stdext::millisleep(100);
}*/ }
} }
return false; return false;
@ -94,8 +99,8 @@ bool DatabaseMySQL::beginTransaction()
bool DatabaseMySQL::rollback() bool DatabaseMySQL::rollback()
{ {
if(mysql_rollback(&m_mysqlHandle) != 0) { if(mysql_rollback(m_handle)) {
g_logger.error(mysql_error(&m_mysqlHandle)); g_logger.error(stdext::format("[DatabaseMySQL::rollback] ERROR: %s (%s)", mysql_error(m_handle), mysql_errno(m_handle)));
return false; return false;
} }
@ -104,8 +109,8 @@ bool DatabaseMySQL::rollback()
bool DatabaseMySQL::commit() bool DatabaseMySQL::commit()
{ {
if(mysql_commit(&m_mysqlHandle) != 0) { if(mysql_commit(m_handle)) {
g_logger.error(mysql_error(&m_mysqlHandle)); g_logger.error(stdext::format("[DatabaseMySQL::commit] ERROR: %s (%s)", mysql_error(m_handle), mysql_errno(m_handle)));
return false; return false;
} }
@ -114,7 +119,7 @@ bool DatabaseMySQL::commit()
bool DatabaseMySQL::internalExecuteQuery(const std::string &query) bool DatabaseMySQL::internalExecuteQuery(const std::string &query)
{ {
while(mysql_real_query(&m_mysqlHandle, query.c_str(), query.length()) != 0) { while(mysql_real_query(m_handle, query.c_str(), query.length()) != 0) {
if(!handleError()) { if(!handleError()) {
return false; return false;
} }
@ -125,14 +130,12 @@ bool DatabaseMySQL::internalExecuteQuery(const std::string &query)
bool DatabaseMySQL::executeQuery(const std::string &query) bool DatabaseMySQL::executeQuery(const std::string &query)
{ {
//LOG_ONDELAY(500);
if(internalExecuteQuery(query)) { if(internalExecuteQuery(query)) {
MYSQL_RES *m_res = mysql_store_result(&m_mysqlHandle); MYSQL_RES* res = mysql_store_result(m_handle);
if(m_res) { if(res) {
mysql_free_result(m_res); mysql_free_result(res);
} else if(mysql_errno(&m_mysqlHandle) != 0) { } else if(mysql_errno(m_handle) != 0) {
handleError(); handleError();
} }
@ -142,129 +145,133 @@ bool DatabaseMySQL::executeQuery(const std::string &query)
return false; return false;
} }
DBResultPtr DatabaseMySQL::storeQuery(const std::string &query) DBResultPtr DatabaseMySQL::storeQuery(const std::string& query)
{ {
//LOG_ONDELAY(500);
while(internalExecuteQuery(query)) { while(internalExecuteQuery(query)) {
MYSQL_RES *m_res = mysql_store_result(&m_mysqlHandle); MYSQL_RES* res = mysql_store_result(m_handle);
if(m_res) { if(res) {
DBResultPtr res = DBResultPtr(new DBResult(m_res)); DBResultPtr result = (DBResultPtr)MySQLResultPtr(new MySQLResult(res));
if(res->next()) { if(!verifyResult(result))
return res;
} else {
//delete res;
break; break;
}
} else if(mysql_errno(&m_mysqlHandle) != 0) { return result;
if(!handleError()) { }
else if(mysql_errno(m_handle) != 0) {
if(!handleError())
break; break;
}
} }
//mSleep(10); stdext::millisleep(10);
} }
return NULL; return nullptr;
} }
uint64_t DatabaseMySQL::getLastInsertedRowID() uint64 DatabaseMySQL::getLastInsertedRowID()
{ {
return (uint64_t)mysql_insert_id(&m_mysqlHandle); return (uint64)mysql_insert_id(m_handle);
} }
std::string DatabaseMySQL::escapeString(const std::string &s) std::string DatabaseMySQL::escapeString(const std::string &s)
{ {
return escapeBlob(s.c_str(), s.length()); return escapeBlob( s.c_str(), s.length() );
} }
std::string DatabaseMySQL::escapeBlob(const char* s, uint32_t length) std::string DatabaseMySQL::escapeBlob(const char* s, uint32 length)
{ {
if(!s) if(!s) {
return std::string("''"); return std::string();
}
char* output = new char[length * 2 + 1]; char* output = new char[length * 2 + 1];
mysql_real_escape_string(m_handle, output, s, length);
std::string res = "'";
res += output;
res += "'";
mysql_real_escape_string(&m_mysqlHandle, output, s, length);
std::string r = "'";
r += output;
r += "'";
delete[] output; delete[] output;
return r; return res;
} }
void DatabaseMySQL::freeResult(DBResult* res) int32 MySQLResult::getDataInt(const std::string& s)
{ {
delete res; RowNames_t::iterator it = m_names.find(s);
if(it != m_names.end())
return m_row[it->second] ? atoi(m_row[it->second]) : 0;
g_logger.error(stdext::format("[MySQLResult::getDataInt] Error: %d", s));
return 0;
} }
DBResult::DBResult(MYSQL_RES* res) int64 MySQLResult::getDataLong(const std::string& s)
{ {
m_res = res; RowNames_t::iterator it = m_names.find(s);
m_listNames.clear(); if(it != m_names.end())
return m_row[it->second] ? atoll(m_row[it->second]) : 0;
g_logger.error(stdext::format("[MySQLResult::getDataLong] Error: %d", s));
return 0;
}
std::string MySQLResult::getDataString(const std::string& s)
{
RowNames_t::iterator it = m_names.find(s);
if(it != m_names.end())
return m_row[it->second] ? std::string(m_row[it->second]) : std::string();
g_logger.error(stdext::format("[MySQLResult::getDataString] Error: %d", s));
return std::string();
}
const char* MySQLResult::getDataStream(const std::string& s, uint64& size)
{
size = 0;
RowNames_t::iterator it = m_names.find(s);
if(it == m_names.end()) {
g_logger.error(stdext::format("[MySQLResult::getDataStream] Error: %d", s));
return NULL;
}
if(!m_row[it->second])
return NULL;
size = mysql_fetch_lengths(m_result)[it->second];
return m_row[it->second];
}
void MySQLResult::free()
{
if(!m_result) {
g_logger.fatal("[MySQLResult::free] Error: trying to free already freed result");
return;
}
mysql_free_result(m_result);
m_result = NULL;
}
bool MySQLResult::next()
{
m_row = mysql_fetch_row(m_result);
return (m_row != NULL);
}
MySQLResult::~MySQLResult()
{
if(m_result)
mysql_free_result(m_result);
}
MySQLResult::MySQLResult(MYSQL_RES* result)
{
m_result = result;
m_names.clear();
MYSQL_FIELD* field; MYSQL_FIELD* field;
int32_t i = 0; int32 i = 0;
while((field = mysql_fetch_field(m_res))) { while((field = mysql_fetch_field(m_result))) {
m_listNames[field->name] = i; m_names[field->name] = i++;
i++;
} }
} }
DBResult::~DBResult()
{
mysql_free_result(m_res);
}
int32_t DBResult::getDataInt(const std::string &s)
{
ListNames::iterator it = m_listNames.find(s);
if(it != m_listNames.end() ) {
if(m_row[it->second] == NULL) {
return 0;
}
else {
return atoi(m_row[it->second]);
}
}
g_logger.error(stdext::format("error during getDataInt(%s).", s));
return 0;
}
int64_t DBResult::getDataLong(const std::string &s)
{
ListNames::iterator it = m_listNames.find(s);
if(it != m_listNames.end()) {
if(m_row[it->second] == NULL) {
return 0;
}
else {
return atoll(m_row[it->second]);
}
}
g_logger.error(stdext::format("error during getDataLong(%s).", s));
return 0;
}
std::string DBResult::getDataString(const std::string &s)
{
ListNames::iterator it = m_listNames.find(s);
if(it != m_listNames.end() ) {
if(m_row[it->second] == NULL)
return std::string("");
else
return std::string(m_row[it->second]);
}
g_logger.error(stdext::format("error during getDataString(%s).", s));
return std::string("");
}
bool DBResult::next()
{
m_row = mysql_fetch_row(m_res);
return m_row != NULL;
}

View File

@ -24,100 +24,68 @@
#define DATABASEMYSQL_H #define DATABASEMYSQL_H
#include "database.h" #include "database.h"
#include <framework/global.h>
#include <mysql/mysql.h>
class DBResult; #include <framework/global.h>
#ifdef WINDOWS
#include <winsock2.h>
#endif
#include <mysql/mysql.h>
class DatabaseMySQL : public Database class DatabaseMySQL : public Database
{ {
public: public:
DatabaseMySQL(); DatabaseMySQL();
~DatabaseMySQL(); virtual ~DatabaseMySQL();
void connect(const std::string& host, const std::string& user, const std::string& pass, virtual void connect(const std::string& host, const std::string& user, const std::string& pass,
const std::string& db, uint16_t port, const std::string& unix_socket = ""); const std::string& db, uint16 port, const std::string& unix_socket = "");
bool beginTransaction(); virtual bool beginTransaction();
bool rollback(); virtual bool rollback();
bool commit(); virtual bool commit();
bool executeQuery(const std::string &query); virtual bool executeQuery(const std::string& query);
DBResultPtr storeQuery(const std::string &query); virtual DBResultPtr storeQuery(const std::string& query);
uint64_t getLastInsertedRowID(); virtual std::string escapeString(const std::string &s);
virtual std::string escapeBlob(const char* s, uint32 length);
std::string escapeString(const std::string &s); virtual uint64 getLastInsertedRowID();
std::string escapeBlob(const char* s, uint32_t length); virtual Fw::DatabaseEngine getDatabaseEngine() {return Fw::DatabaseMySQL;}
void freeResult(DBResult *res);
protected: protected:
bool handleError(); bool handleError();
bool internalExecuteQuery(const std::string &query); bool internalExecuteQuery(const std::string &query);
bool m_running; MYSQL* m_handle;
MYSQL m_mysqlHandle;
}; };
class DBResult : public LuaObject class MySQLResult : public DBResult
{ {
friend class DatabaseMySQL;
public: public:
DBResult(MYSQL_RES* res); MySQLResult(MYSQL_RES* result);
~DBResult(); virtual ~MySQLResult();
friend class DatabaseMySQL; virtual int32 getDataInt(const std::string& s);
virtual int64 getDataLong(const std::string& s);
virtual std::string getDataString(const std::string& s);
virtual const char* getDataStream(const std::string& s, uint64& size);
int32_t getDataInt(const std::string &s); virtual void free();
int64_t getDataLong(const std::string &s); virtual bool next();
std::string getDataString(const std::string &s); virtual int getRowCount() { return mysql_num_rows(m_result); }
bool next(); protected:
int getRowCount() { return mysql_num_rows(m_res); } typedef std::map<const std::string, uint32> RowNames_t;
RowNames_t m_names;
private: MYSQL_RES* m_result;
typedef std::map<const std::string, uint32_t> ListNames;
ListNames m_listNames;
MYSQL_RES* m_res;
MYSQL_ROW m_row; MYSQL_ROW m_row;
}; };
class DBTransaction
{
public:
DBTransaction(DatabaseMySQL* database) {
m_database = database;
m_state = STATE_NO_START;
}
~DBTransaction() {
if(m_state == STATE_START) {
m_database->rollback();
}
}
bool begin() {
m_state = STATE_START;
return m_database->beginTransaction();
}
bool commit() {
if(m_state == STATE_START) {
m_state = STEATE_COMMIT;
return m_database->commit();
} else {
return false;
}
}
private:
enum TransactionStates_t {
STATE_NO_START, STATE_START, STEATE_COMMIT
};
TransactionStates_t m_state;
DatabaseMySQL* m_database;
};
#endif #endif