Add SQL extension, still in early stage
This commit is contained in:
parent
c9eaa73df5
commit
8a5195430a
|
@ -443,5 +443,18 @@ if(FRAMEWORK_XML)
|
||||||
set(framework_DEFINITIONS ${framework_DEFINITIONS} -DFW_XML)
|
set(framework_DEFINITIONS ${framework_DEFINITIONS} -DFW_XML)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(FRAMEWORK_SQL)
|
||||||
|
find_package(Mysql REQUIRED)
|
||||||
|
|
||||||
|
set(framework_INCLUDE_DIRS ${framework_INCLUDE_DIRS} ${MYSQL_INCLUDE_DIR})
|
||||||
|
set(framework_LIBRARIES ${framework_LIBRARIES} ${MYSQL_LIBRARY})
|
||||||
|
|
||||||
|
set(framework_SOURCES ${framework_SOURCES}
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/sql/mysql.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/sql/mysql.h
|
||||||
|
)
|
||||||
|
set(framework_DEFINITIONS ${framework_DEFINITIONS} -DFW_SQL)
|
||||||
|
endif()
|
||||||
|
|
||||||
include_directories(${framework_INCLUDE_DIRS})
|
include_directories(${framework_INCLUDE_DIRS})
|
||||||
add_definitions(${framework_DEFINITIONS})
|
add_definitions(${framework_DEFINITIONS})
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
# - Find mysqlclient
|
||||||
|
# Find the native MySQL includes and library
|
||||||
|
#
|
||||||
|
# MYSQL_INCLUDE_DIR - where to find mysql.h, etc.
|
||||||
|
# MYSQL_LIBRARIES - List of libraries when using MySQL.
|
||||||
|
# MYSQL_FOUND - True if MySQL found.
|
||||||
|
|
||||||
|
IF (MYSQL_INCLUDE_DIR)
|
||||||
|
# Already in cache, be silent
|
||||||
|
SET(MYSQL_FIND_QUIETLY TRUE)
|
||||||
|
ENDIF (MYSQL_INCLUDE_DIR)
|
||||||
|
|
||||||
|
FIND_PATH(MYSQL_INCLUDE_DIR NAMES mysql.h
|
||||||
|
PATH_SUFFIXES mysql
|
||||||
|
/usr/local/include/mysql
|
||||||
|
/usr/include/mysql
|
||||||
|
)
|
||||||
|
|
||||||
|
SET(MYSQL_NAMES mysqlclient mysqlclient_r libmysql.a)
|
||||||
|
FIND_LIBRARY(MYSQL_LIBRARY
|
||||||
|
NAMES ${MYSQL_NAMES}
|
||||||
|
PATHS /usr/lib /usr/local/lib
|
||||||
|
PATH_SUFFIXES mysql
|
||||||
|
)
|
||||||
|
|
||||||
|
IF (MYSQL_INCLUDE_DIR AND MYSQL_LIBRARY)
|
||||||
|
SET(MYSQL_FOUND TRUE)
|
||||||
|
SET( MYSQL_LIBRARIES ${MYSQL_LIBRARY} )
|
||||||
|
ELSE (MYSQL_INCLUDE_DIR AND MYSQL_LIBRARY)
|
||||||
|
SET(MYSQL_FOUND FALSE)
|
||||||
|
SET( MYSQL_LIBRARIES )
|
||||||
|
ENDIF (MYSQL_INCLUDE_DIR AND MYSQL_LIBRARY)
|
||||||
|
|
||||||
|
IF (MYSQL_FOUND)
|
||||||
|
IF (NOT MYSQL_FIND_QUIETLY)
|
||||||
|
MESSAGE(STATUS "Found MySQL: ${MYSQL_LIBRARY}")
|
||||||
|
ENDIF (NOT MYSQL_FIND_QUIETLY)
|
||||||
|
ELSE (MYSQL_FOUND)
|
||||||
|
IF (MYSQL_FIND_REQUIRED)
|
||||||
|
MESSAGE(STATUS "Looked for MySQL libraries named ${MYSQL_NAMES}.")
|
||||||
|
MESSAGE(FATAL_ERROR "Could NOT find MySQL library")
|
||||||
|
ENDIF (MYSQL_FIND_REQUIRED)
|
||||||
|
ENDIF (MYSQL_FOUND)
|
||||||
|
|
||||||
|
MARK_AS_ADVANCED(
|
||||||
|
MYSQL_LIBRARY
|
||||||
|
MYSQL_INCLUDE_DIR
|
||||||
|
)
|
||||||
|
|
|
@ -0,0 +1,264 @@
|
||||||
|
#include "mysql.h"
|
||||||
|
#include <mysql/errmsg.h>
|
||||||
|
#include <framework/core/clock.h>
|
||||||
|
#include <framework/core/logger.h>
|
||||||
|
|
||||||
|
DatabaseMySQL::DatabaseMySQL() : m_running(false)
|
||||||
|
{
|
||||||
|
if(!mysql_init(&m_mysqlHandle))
|
||||||
|
g_logger.fatal("Failed to initialize MySQL connection handle.");
|
||||||
|
|
||||||
|
my_bool reconnect = true;
|
||||||
|
mysql_options(&m_mysqlHandle, MYSQL_OPT_RECONNECT, &reconnect);
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseMySQL::~DatabaseMySQL()
|
||||||
|
{
|
||||||
|
mysql_close(&m_mysqlHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if(!mysql_real_connect(&m_mysqlHandle,
|
||||||
|
host.c_str(),
|
||||||
|
user.c_str(),
|
||||||
|
pass.c_str(),
|
||||||
|
db.c_str(),
|
||||||
|
port,
|
||||||
|
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)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseMySQL::handleError()
|
||||||
|
{
|
||||||
|
unsigned int error = mysql_errno(&m_mysqlHandle);
|
||||||
|
g_logger.error(stdext::format("MYSQL error code = %d, message: %s", error, mysql_error(&m_mysqlHandle)));
|
||||||
|
|
||||||
|
if(error == CR_SOCKET_CREATE_ERROR ||
|
||||||
|
error == CR_CONNECTION_ERROR ||
|
||||||
|
error == CR_CONN_HOST_ERROR ||
|
||||||
|
error == CR_IPSOCK_ERROR ||
|
||||||
|
error == CR_UNKNOWN_HOST ||
|
||||||
|
error == CR_SERVER_GONE_ERROR ||
|
||||||
|
error == CR_SERVER_LOST ||
|
||||||
|
error == CR_SERVER_HANDSHAKE_ERR) {
|
||||||
|
g_logger.error("MYSQL connection lost, trying to reconnect...");
|
||||||
|
|
||||||
|
//int64_t startTime = g_clock.millis();
|
||||||
|
|
||||||
|
/*while(true) {
|
||||||
|
bool connected = (mysql_ping(&m_mysqlHandle) == 0);
|
||||||
|
uint32_t diffTime = (mTime() - startTime);
|
||||||
|
if(connected) {
|
||||||
|
g_logger.info(stdext::format("MySQL reconneted in %d ms", diffTime));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
mSleep(100);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseMySQL::beginTransaction()
|
||||||
|
{
|
||||||
|
return executeQuery("BEGIN");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseMySQL::rollback()
|
||||||
|
{
|
||||||
|
if(mysql_rollback(&m_mysqlHandle) != 0) {
|
||||||
|
g_logger.error(mysql_error(&m_mysqlHandle));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseMySQL::commit()
|
||||||
|
{
|
||||||
|
if(mysql_commit(&m_mysqlHandle) != 0) {
|
||||||
|
g_logger.error(mysql_error(&m_mysqlHandle));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseMySQL::internalExecuteQuery(const std::string &query)
|
||||||
|
{
|
||||||
|
while(mysql_real_query(&m_mysqlHandle, query.c_str(), query.length()) != 0) {
|
||||||
|
if(!handleError()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseMySQL::executeQuery(const std::string &query)
|
||||||
|
{
|
||||||
|
//LOG_ONDELAY(500);
|
||||||
|
|
||||||
|
if(internalExecuteQuery(query)) {
|
||||||
|
MYSQL_RES *m_res = mysql_store_result(&m_mysqlHandle);
|
||||||
|
|
||||||
|
if(m_res) {
|
||||||
|
mysql_free_result(m_res);
|
||||||
|
} else if(mysql_errno(&m_mysqlHandle) != 0) {
|
||||||
|
handleError();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBResult* DatabaseMySQL::storeQuery(const std::string &query)
|
||||||
|
{
|
||||||
|
//LOG_ONDELAY(500);
|
||||||
|
|
||||||
|
while(internalExecuteQuery(query)) {
|
||||||
|
MYSQL_RES *m_res = mysql_store_result(&m_mysqlHandle);
|
||||||
|
|
||||||
|
if(m_res) {
|
||||||
|
DBResult* res = new DBResult(m_res);
|
||||||
|
if(res->next()) {
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
delete res;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if(mysql_errno(&m_mysqlHandle) != 0) {
|
||||||
|
if(!handleError()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//mSleep(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t DatabaseMySQL::getLastInsertedRowID()
|
||||||
|
{
|
||||||
|
return (uint64_t)mysql_insert_id(&m_mysqlHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DatabaseMySQL::escapeString(const std::string &s)
|
||||||
|
{
|
||||||
|
return escapeBlob( s.c_str(), s.length() );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DatabaseMySQL::escapeBlob(const char* s, uint32_t length)
|
||||||
|
{
|
||||||
|
if(!s)
|
||||||
|
return std::string("''");
|
||||||
|
|
||||||
|
char* output = new char[length * 2 + 1];
|
||||||
|
|
||||||
|
mysql_real_escape_string(&m_mysqlHandle, output, s, length);
|
||||||
|
std::string r = "'";
|
||||||
|
r += output;
|
||||||
|
r += "'";
|
||||||
|
delete[] output;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseMySQL::freeResult(DBResult* res)
|
||||||
|
{
|
||||||
|
delete res;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBResult::DBResult(MYSQL_RES* res)
|
||||||
|
{
|
||||||
|
m_res = res;
|
||||||
|
m_listNames.clear();
|
||||||
|
|
||||||
|
MYSQL_FIELD* field;
|
||||||
|
int32_t i = 0;
|
||||||
|
while((field = mysql_fetch_field(m_res))) {
|
||||||
|
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("");
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* DBResult::getDataStream(const std::string &s, unsigned long &size)
|
||||||
|
{
|
||||||
|
ListNames::iterator it = m_listNames.find(s);
|
||||||
|
if(it != m_listNames.end() ) {
|
||||||
|
if(m_row[it->second] == NULL) {
|
||||||
|
size = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
size = mysql_fetch_lengths(m_res)[it->second];
|
||||||
|
return m_row[it->second];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_logger.error(stdext::format("error during getDataStream(%s).", s));
|
||||||
|
size = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DBResult::next()
|
||||||
|
{
|
||||||
|
m_row = mysql_fetch_row(m_res);
|
||||||
|
return m_row != NULL;
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
#ifndef DATABASE_H
|
||||||
|
#define DATABASE_H
|
||||||
|
|
||||||
|
#include <framework/global.h>
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#endif
|
||||||
|
#include <mysql/mysql.h>
|
||||||
|
|
||||||
|
class DBResult;
|
||||||
|
|
||||||
|
class DatabaseMySQL
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DatabaseMySQL();
|
||||||
|
~DatabaseMySQL();
|
||||||
|
|
||||||
|
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 = "");
|
||||||
|
|
||||||
|
bool beginTransaction();
|
||||||
|
bool rollback();
|
||||||
|
bool commit();
|
||||||
|
|
||||||
|
bool executeQuery(const std::string &query);
|
||||||
|
DBResult* storeQuery(const std::string &query);
|
||||||
|
|
||||||
|
uint64_t getLastInsertedRowID();
|
||||||
|
|
||||||
|
std::string escapeString(const std::string &s);
|
||||||
|
std::string escapeBlob(const char* s, uint32_t length);
|
||||||
|
|
||||||
|
void freeResult(DBResult *res);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool handleError();
|
||||||
|
bool internalExecuteQuery(const std::string &query);
|
||||||
|
|
||||||
|
bool m_running;
|
||||||
|
MYSQL m_mysqlHandle;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DBResult
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
DBResult(MYSQL_RES* res);
|
||||||
|
~DBResult();
|
||||||
|
|
||||||
|
friend class DatabaseMySQL;
|
||||||
|
|
||||||
|
public:
|
||||||
|
int32_t getDataInt(const std::string &s);
|
||||||
|
int64_t getDataLong(const std::string &s);
|
||||||
|
std::string getDataString(const std::string &s);
|
||||||
|
const char* getDataStream(const std::string &s, unsigned long &size);
|
||||||
|
|
||||||
|
bool next();
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef std::map<const std::string, uint32_t> ListNames;
|
||||||
|
ListNames m_listNames;
|
||||||
|
|
||||||
|
MYSQL_RES* m_res;
|
||||||
|
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
|
Loading…
Reference in New Issue