//////////////////////////////////////////////////////////////////////// // OpenTibia - an opensource roleplaying game //////////////////////////////////////////////////////////////////////// // 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 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . //////////////////////////////////////////////////////////////////////// #ifndef __DATABASE__ #define __DATABASE__ #include "otsystem.h" #include "enums.h" #include #ifdef MULTI_SQL_DRIVERS #define DATABASE_VIRTUAL virtual #define DATABASE_CLASS _Database #define DBRES_CLASS _DBResult class _Database; class _DBResult; #else #define DATABASE_VIRTUAL #if defined(__USE_MYSQL__) #define DATABASE_CLASS DatabaseMySQL #define DBRES_CLASS MySQLResult class DatabaseMySQL; class MySQLResult; #elif defined(__USE_SQLITE__) #define DATABASE_CLASS DatabaseSQLite #define DBRES_CLASS SQLiteResult class DatabaseSQLite; class SQLiteResult; #elif defined(__USE_ODBC__) #define DATABASE_CLASS DatabaseODBC #define DBRES_CLASS ODBCResult class DatabaseODBC; class ODBCResult; #elif defined(__USE_PGSQL__) #define DATABASE_CLASS DatabasePgSQL #define DBRES_CLASS PgSQLResult class DatabasePgSQL; class PgSQLResult; #endif #endif #ifndef DATABASE_CLASS #error "You have to compile with at least one database driver!" #define DBResult void #define DBInsert void* #define Database void #else typedef DATABASE_CLASS Database; typedef DBRES_CLASS DBResult; class DBQuery; enum DBParam_t { DBPARAM_MULTIINSERT = 1 }; class _Database { public: /** * 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 exacly one instance of connection is created for entire system. * * @return database connection handler singletor */ static Database* getInstance(); /** * Database information. * * Returns currently used database attribute. * * @param DBParam_t parameter to get * @return suitable for given parameter */ DATABASE_VIRTUAL bool getParam(DBParam_t param) {return false;} /** * Database connected. * * Returns whether or not the database is connected. * * @return whether or not the database is connected. */ DATABASE_VIRTUAL bool isConnected() {return m_connected;} /** * Database ... */ DATABASE_VIRTUAL void use() {m_use = time(NULL);} 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 * @note * 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. */ friend class DBTransaction; DATABASE_VIRTUAL bool beginTransaction() {return 0;} DATABASE_VIRTUAL bool rollback() {return 0;} DATABASE_VIRTUAL bool commit() {return 0;} public: /** * 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 */ DATABASE_VIRTUAL bool executeQuery(const std::string &query) {return 0;} /** * Queries database. * * Executes query which generates results (mostly SELECT). * * @param std::string query * @return results object (null on error) */ DATABASE_VIRTUAL DBResult* storeQuery(const std::string &query) {return 0;} /** * Escapes string for query. * * Prepares string to fit SQL queries including quoting it. * * @param std::string string to be escaped * @return quoted string */ DATABASE_VIRTUAL std::string escapeString(const std::string &s) {return "''";} /** * Escapes binary stream for query. * * Prepares binary stream to fit SQL queries. * * @param char* binary stream * @param long stream length * @return quoted string */ DATABASE_VIRTUAL std::string escapeBlob(const char* s, uint32_t length) {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 */ DATABASE_VIRTUAL uint64_t getLastInsertId() {return 0;} /** * Get case insensitive string comparison operator * * @return the case insensitive operator */ DATABASE_VIRTUAL std::string getStringComparison() {return "=";} DATABASE_VIRTUAL std::string getUpdateLimiter() {return " LIMIT 1";} /** * Get database engine * * @return the database engine type */ DATABASE_VIRTUAL DatabaseEngine_t getDatabaseEngine() {return DATABASE_ENGINE_NONE;} protected: _Database() {} DATABASE_VIRTUAL ~_Database() {} DBResult* verifyResult(DBResult* result); bool m_connected; time_t m_use; private: static Database* _instance; }; class _DBResult { 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 */ DATABASE_VIRTUAL int32_t getDataInt(const std::string &s) {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 */ DATABASE_VIRTUAL int64_t getDataLong(const std::string &s) {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 */ DATABASE_VIRTUAL std::string getDataString(const std::string &s) {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 */ DATABASE_VIRTUAL const char* getDataStream(const std::string &s, uint64_t &size) {return 0;} /** Result freeing */ DATABASE_VIRTUAL void free() {/*delete this;*/} /** Moves to next result in set *\returns true if moved, false if there are no more results. */ DATABASE_VIRTUAL bool next() {return false;} protected: _DBResult() {} DATABASE_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() {OTSYS_THREAD_LOCK(databaseLock, "");} virtual ~DBQuery() {str(""); OTSYS_THREAD_UNLOCK(databaseLock, "");} protected: static OTSYS_THREAD_LOCKVAR 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(Database* db); virtual ~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: Database* m_db; bool m_multiLine; uint32_t m_rows; std::string m_query, m_buf; }; #ifndef MULTI_SQL_DRIVERS #if defined(__USE_MYSQL__) #include "databasemysql.h" #elif defined(__USE_SQLITE__) #include "databasesqlite.h" #elif defined(__USE_ODBC__) #include "databaseodbc.h" #elif defined(__USE_PGSQL__) #include "databasepgsql.h" #endif #endif class DBTransaction { public: DBTransaction(Database* database) { m_database = database; m_state = STATE_NO_START; } virtual ~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) return false; m_state = STEATE_COMMIT; return m_database->commit(); } private: enum TransactionStates_t { STATE_NO_START, STATE_START, STEATE_COMMIT }; TransactionStates_t m_state; Database* m_database; }; #endif #endif