SabrehavenServer/src/database.h
2019-09-16 20:38:16 +03:00

244 lines
5.3 KiB
C++

/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef FS_DATABASE_H_A484B0CDFDE542838F506DCE3D40C693
#define FS_DATABASE_H_A484B0CDFDE542838F506DCE3D40C693
#include <boost/lexical_cast.hpp>
#include <mysql/mysql.h>
class DBResult;
typedef std::shared_ptr<DBResult> DBResult_ptr;
class Database
{
public:
Database() = default;
~Database();
// non-copyable
Database(const Database&) = delete;
Database& operator=(const Database&) = delete;
/**
* Singleton implementation.
*
* @return database connection handler singleton
*/
static Database* getInstance()
{
static Database instance;
return &instance;
}
/**
* Connects to the database
*
* @return true on successful connection, false on error
*/
bool connect();
/**
* Executes command.
*
* Executes query which doesn't generates results (eg. INSERT, UPDATE, DELETE...).
*
* @param query command
* @return true on success, false on error
*/
bool executeQuery(const std::string& query);
/**
* Queries database.
*
* Executes query which generates results (mostly SELECT).
*
* @return results object (nullptr on error)
*/
DBResult_ptr storeQuery(const std::string& query);
/**
* Escapes string for query.
*
* Prepares string to fit SQL queries including quoting it.
*
* @param s string to be escaped
* @return quoted string
*/
std::string escapeString(const std::string& s) const;
/**
* Escapes binary stream for query.
*
* Prepares binary stream to fit SQL queries.
*
* @param s binary stream
* @param length stream length
* @return quoted string
*/
std::string escapeBlob(const char* s, uint32_t length) const;
/**
* Retrieve id of last inserted row
*
* @return id on success, 0 if last query did not result on any rows with auto_increment keys
*/
uint64_t getLastInsertId() const {
return static_cast<uint64_t>(mysql_insert_id(handle));
}
/**
* Get database engine version
*
* @return the database engine version
*/
static const char* getClientVersion() {
return mysql_get_client_info();
}
uint64_t getMaxPacketSize() const {
return maxPacketSize;
}
protected:
/**
* Transaction related methods.
*
* Methods for starting, commiting and rolling back transaction. Each of the returns boolean value.
*
* @return true on success, false on error
*/
bool beginTransaction();
bool rollback();
bool commit();
private:
MYSQL* handle = nullptr;
std::recursive_mutex databaseLock;
uint64_t maxPacketSize = 1048576;
friend class DBTransaction;
};
class DBResult
{
public:
explicit DBResult(MYSQL_RES* res);
~DBResult();
// non-copyable
DBResult(const DBResult&) = delete;
DBResult& operator=(const DBResult&) = delete;
template<typename T>
T getNumber(const std::string& s) const
{
auto it = listNames.find(s);
if (it == listNames.end()) {
std::cout << "[Error - DBResult::getNumber] Column '" << s << "' doesn't exist in the result set" << std::endl;
return static_cast<T>(0);
}
if (row[it->second] == nullptr) {
return static_cast<T>(0);
}
T data;
try {
data = boost::lexical_cast<T>(row[it->second]);
} catch (boost::bad_lexical_cast&) {
data = 0;
}
return data;
}
std::string getString(const std::string& s) const;
const char* getStream(const std::string& s, unsigned long& size) const;
bool hasNext() const;
bool next();
private:
MYSQL_RES* handle;
MYSQL_ROW row;
std::map<std::string, size_t> listNames;
friend class Database;
};
/**
* INSERT statement.
*/
class DBInsert
{
public:
explicit DBInsert(std::string query);
bool addRow(const std::string& row);
bool addRow(std::ostringstream& row);
bool execute();
protected:
std::string query;
std::string values;
size_t length;
};
class DBTransaction
{
public:
constexpr DBTransaction() = default;
~DBTransaction() {
if (state == STATE_START) {
Database::getInstance()->rollback();
}
}
// non-copyable
DBTransaction(const DBTransaction&) = delete;
DBTransaction& operator=(const DBTransaction&) = delete;
bool begin() {
state = STATE_START;
return Database::getInstance()->beginTransaction();
}
bool commit() {
if (state != STATE_START) {
return false;
}
state = STEATE_COMMIT;
return Database::getInstance()->commit();
}
private:
enum TransactionStates_t {
STATE_NO_START,
STATE_START,
STEATE_COMMIT,
};
TransactionStates_t state = STATE_NO_START;
};
#endif