Work on sql framework classes
This commit is contained in:
parent
3980f859b7
commit
02d32565e9
|
@ -7,6 +7,7 @@ set(FRAMEWORK_SOUND ON)
|
||||||
set(FRAMEWORK_GRAPHICS ON)
|
set(FRAMEWORK_GRAPHICS ON)
|
||||||
set(FRAMEWORK_XML ON)
|
set(FRAMEWORK_XML ON)
|
||||||
set(FRAMEWORK_NET ON)
|
set(FRAMEWORK_NET ON)
|
||||||
|
set(FRAMEWORK_SQL ON)
|
||||||
include(src/framework/CMakeLists.txt)
|
include(src/framework/CMakeLists.txt)
|
||||||
include(src/client/CMakeLists.txt)
|
include(src/client/CMakeLists.txt)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -21,11 +21,69 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database.h"
|
#include "database.h"
|
||||||
|
#include "mysql.h"
|
||||||
|
|
||||||
Database::Database()
|
boost::recursive_mutex DBQuery::databaseLock;
|
||||||
|
DatabasePtr Database::m_instance = nullptr;
|
||||||
|
|
||||||
|
DatabasePtr Database::getInstance()
|
||||||
{
|
{
|
||||||
|
if(!m_instance)
|
||||||
|
m_instance = (DatabasePtr)DatabaseMySQLPtr(new DatabaseMySQL());
|
||||||
|
|
||||||
|
m_instance->use();
|
||||||
|
return m_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
Database::~Database()
|
DBResultPtr Database::verifyResult(DBResultPtr result)
|
||||||
{
|
{
|
||||||
|
if(result->next())
|
||||||
|
return result;
|
||||||
|
|
||||||
|
result->free();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,16 +22,299 @@
|
||||||
|
|
||||||
#ifndef DATABASE_H
|
#ifndef DATABASE_H
|
||||||
#define DATABASE_H
|
#define DATABASE_H
|
||||||
|
|
||||||
#include "declarations.h"
|
#include "declarations.h"
|
||||||
#include <framework/global.h>
|
|
||||||
|
#include <framework/core/clock.h>
|
||||||
#include <framework/luaengine/luaobject.h>
|
#include <framework/luaengine/luaobject.h>
|
||||||
|
|
||||||
|
#include <boost/thread.hpp>
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
class Database : public LuaObject
|
class Database : public LuaObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Database();
|
friend class DBTransaction;
|
||||||
~Database();
|
|
||||||
|
/**
|
||||||
|
* Singleton implementation.
|
||||||
|
*
|
||||||
|
* Retruns instance of database handler. Don't create database (or drivers) instances in your code - instead of it use Database::getInstance()->
|
||||||
|
* This method stores static instance of connection class internaly to make sure exactly one instance of connection is created for entire system.
|
||||||
|
*
|
||||||
|
* @return database connection handler singleton
|
||||||
|
*/
|
||||||
|
static DatabasePtr getInstance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database ...
|
||||||
|
*/
|
||||||
|
virtual void use() {m_use = g_clock.millis();}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() const {return m_connected;}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database set connected.
|
||||||
|
*
|
||||||
|
* Sets the database to know that it is connected.
|
||||||
|
*/
|
||||||
|
void setConnected(bool connected) { m_connected = connected; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool handleError() {return false;}
|
||||||
|
virtual bool internalExecuteQuery(const std::string &query) {return false;}
|
||||||
|
|
||||||
|
DBResultPtr verifyResult(DBResultPtr result);
|
||||||
|
|
||||||
|
Database(): m_connected(false) {}
|
||||||
|
virtual ~Database() {m_connected = false;}
|
||||||
|
|
||||||
|
ticks_t m_use;
|
||||||
|
bool m_connected;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static DatabasePtr m_instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DBResult : public LuaObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** 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; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
DBResult() {}
|
||||||
|
virtual ~DBResult() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread locking hack.
|
||||||
|
*
|
||||||
|
* By using this class for your queries you lock and unlock database for threads.
|
||||||
|
*/
|
||||||
|
class DBQuery : public std::stringstream
|
||||||
|
{
|
||||||
|
friend class Database;
|
||||||
|
public:
|
||||||
|
DBQuery() {databaseLock.lock();}
|
||||||
|
~DBQuery() {databaseLock.unlock();}
|
||||||
|
|
||||||
|
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 Database* database wrapper
|
||||||
|
*/
|
||||||
|
DBInsert(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, 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
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2010-2013 OTClient <https://github.com/edubart/otclient>
|
* Copyright (c) 2010-2012 TitanCore <https://github.com/edubart/titancore>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -25,10 +25,16 @@
|
||||||
|
|
||||||
#include <framework/global.h>
|
#include <framework/global.h>
|
||||||
|
|
||||||
|
class Database;
|
||||||
class DBResult;
|
class DBResult;
|
||||||
class DatabaseMySQL;
|
|
||||||
|
|
||||||
typedef stdext::shared_object_ptr<DBResult> DBResultPtr;
|
class DatabaseMySQL;
|
||||||
|
class MySQLResult;
|
||||||
|
|
||||||
|
typedef stdext::shared_object_ptr<Database> DatabasePtr;
|
||||||
typedef stdext::shared_object_ptr<DatabaseMySQL> DatabaseMySQLPtr;
|
typedef stdext::shared_object_ptr<DatabaseMySQL> DatabaseMySQLPtr;
|
||||||
|
|
||||||
|
typedef stdext::shared_object_ptr<DBResult> DBResultPtr;
|
||||||
|
typedef stdext::shared_object_ptr<MySQLResult> MySQLResultPtr;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -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 = g_clock.millis();
|
||||||
|
while(true) {
|
||||||
/*while(true) {
|
bool connected = (mysql_ping(m_handle) == 0);
|
||||||
bool connected = (mysql_ping(&m_mysqlHandle) == 0);
|
ticks_t diffTime = (g_clock.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;
|
||||||
}
|
}
|
||||||
|
@ -128,11 +133,11 @@ bool DatabaseMySQL::executeQuery(const std::string &query)
|
||||||
//LOG_ONDELAY(500);
|
//LOG_ONDELAY(500);
|
||||||
|
|
||||||
if(internalExecuteQuery(query)) {
|
if(internalExecuteQuery(query)) {
|
||||||
MYSQL_RES *m_res = mysql_store_result(&m_mysqlHandle);
|
MYSQL_RES* m_res = mysql_store_result(m_handle);
|
||||||
|
|
||||||
if(m_res) {
|
if(m_res) {
|
||||||
mysql_free_result(m_res);
|
mysql_free_result(m_res);
|
||||||
} else if(mysql_errno(&m_mysqlHandle) != 0) {
|
} else if(mysql_errno(m_handle) != 0) {
|
||||||
handleError();
|
handleError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,124 +152,132 @@ DBResultPtr DatabaseMySQL::storeQuery(const std::string &query)
|
||||||
//LOG_ONDELAY(500);
|
//LOG_ONDELAY(500);
|
||||||
|
|
||||||
while(internalExecuteQuery(query)) {
|
while(internalExecuteQuery(query)) {
|
||||||
MYSQL_RES *m_res = mysql_store_result(&m_mysqlHandle);
|
MYSQL_RES* m_res = mysql_store_result(m_handle);
|
||||||
|
|
||||||
if(m_res) {
|
if(m_res) {
|
||||||
DBResultPtr res = DBResultPtr(new DBResult(m_res));
|
DBResultPtr res = (DBResultPtr)MySQLResultPtr(new MySQLResult(m_res));
|
||||||
if(res->next()) {
|
if(!verifyResult(res))
|
||||||
return res;
|
|
||||||
} else {
|
|
||||||
//delete res;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
} else if(mysql_errno(&m_mysqlHandle) != 0) {
|
return res;
|
||||||
if(!handleError()) {
|
}
|
||||||
|
else if(mysql_errno(m_handle) != 0) {
|
||||||
|
if(!handleError())
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//mSleep(10);
|
//mSleep(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;
|
listNames_t::iterator it = m_listNames.find(s);
|
||||||
|
if(it != m_listNames.end())
|
||||||
|
return m_row[it->second] ? atoi(m_row[it->second]) : 0;
|
||||||
|
|
||||||
|
g_logger.error(stdext::format("MySQLResult::getDataInt() Error: %d", s));
|
||||||
|
return 0; // Failed
|
||||||
}
|
}
|
||||||
|
|
||||||
DBResult::DBResult(MYSQL_RES* res)
|
int64 MySQLResult::getDataLong(const std::string& s)
|
||||||
{
|
{
|
||||||
m_res = res;
|
listNames_t::iterator it = m_listNames.find(s);
|
||||||
|
if(it != m_listNames.end())
|
||||||
|
return m_row[it->second] ? atoll(m_row[it->second]) : 0;
|
||||||
|
|
||||||
|
g_logger.error(stdext::format("MySQLResult::getDataLong() Error: %d", s));
|
||||||
|
return 0; // Failed
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MySQLResult::getDataString(const std::string& s)
|
||||||
|
{
|
||||||
|
listNames_t::iterator it = m_listNames.find(s);
|
||||||
|
if(it != m_listNames.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(); // Failed
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* MySQLResult::getDataStream(const std::string& s, uint64& size)
|
||||||
|
{
|
||||||
|
size = 0;
|
||||||
|
listNames_t::iterator it = m_listNames.find(s);
|
||||||
|
if(it == m_listNames.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_resultHandle)[it->second];
|
||||||
|
return m_row[it->second];
|
||||||
|
}
|
||||||
|
|
||||||
|
void MySQLResult::free()
|
||||||
|
{
|
||||||
|
if(!m_resultHandle) {
|
||||||
|
g_logger.fatal("MySQLResult::free() Error trying to free already freed result");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mysql_free_result(m_resultHandle);
|
||||||
|
|
||||||
|
delete m_resultHandle;
|
||||||
|
m_resultHandle = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MySQLResult::next()
|
||||||
|
{
|
||||||
|
m_row = mysql_fetch_row(m_resultHandle);
|
||||||
|
return (m_row != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
MySQLResult::~MySQLResult()
|
||||||
|
{
|
||||||
|
if(m_resultHandle)
|
||||||
|
mysql_free_result(m_resultHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
MySQLResult::MySQLResult(MYSQL_RES* result)
|
||||||
|
{
|
||||||
|
m_resultHandle = result;
|
||||||
m_listNames.clear();
|
m_listNames.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_resultHandle))) {
|
||||||
m_listNames[field->name] = i;
|
m_listNames[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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -20,104 +20,70 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef DATABASEMYSQL_H
|
#ifndef __DATABASE_MYSQL__
|
||||||
#define DATABASEMYSQL_H
|
#define __DATABASE_MYSQL__
|
||||||
|
|
||||||
#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:
|
||||||
|
bool handleError();
|
||||||
|
bool internalExecuteQuery(const std::string &query);
|
||||||
|
|
||||||
protected:
|
MYSQL* m_handle;
|
||||||
bool handleError();
|
|
||||||
bool internalExecuteQuery(const std::string &query);
|
|
||||||
|
|
||||||
bool m_running;
|
|
||||||
MYSQL m_mysqlHandle;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DBResult : public LuaObject
|
class MySQLResult : public DBResult
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
DBResult(MYSQL_RES* res);
|
|
||||||
~DBResult();
|
|
||||||
|
|
||||||
friend class DatabaseMySQL;
|
friend class DatabaseMySQL;
|
||||||
|
public:
|
||||||
|
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_resultHandle); }
|
||||||
|
|
||||||
bool next();
|
protected:
|
||||||
int getRowCount() { return mysql_num_rows(m_res); }
|
MySQLResult(MYSQL_RES* result);
|
||||||
|
virtual ~MySQLResult();
|
||||||
|
|
||||||
private:
|
typedef std::map<const std::string, uint32> listNames_t;
|
||||||
typedef std::map<const std::string, uint32_t> ListNames;
|
listNames_t m_listNames;
|
||||||
ListNames m_listNames;
|
|
||||||
|
|
||||||
MYSQL_RES* m_res;
|
MYSQL_RES* m_resultHandle;
|
||||||
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
|
||||||
|
|
Loading…
Reference in New Issue