diff options
author | Huynh Tran <nthuynh75@gmail.com> | 2005-06-21 19:46:40 +0000 |
---|---|---|
committer | Huynh Tran <nthuynh75@gmail.com> | 2005-06-21 19:46:40 +0000 |
commit | 49d79a345c8c4929d9bb787af9f4f0090c888537 (patch) | |
tree | 387b3a8d738d570ad1493be0273751c7a145cf20 /src | |
parent | 816d8acd16faeb20713c3d09466003a444940f6f (diff) | |
download | manaserv-49d79a345c8c4929d9bb787af9f4f0090c888537.tar.gz manaserv-49d79a345c8c4929d9bb787af9f4f0090c888537.tar.bz2 manaserv-49d79a345c8c4929d9bb787af9f4f0090c888537.tar.xz manaserv-49d79a345c8c4929d9bb787af9f4f0090c888537.zip |
Improved Storage APIs and moved debug code to unit tests.
Diffstat (limited to 'src')
-rw-r--r-- | src/dalstorage.cpp | 259 | ||||
-rw-r--r-- | src/dalstorage.h | 40 | ||||
-rw-r--r-- | src/main.cpp | 4 | ||||
-rw-r--r-- | src/storage.cpp | 76 | ||||
-rw-r--r-- | src/storage.h | 205 |
5 files changed, 452 insertions, 132 deletions
diff --git a/src/dalstorage.cpp b/src/dalstorage.cpp index 1bcafedf..14c3ff7a 100644 --- a/src/dalstorage.cpp +++ b/src/dalstorage.cpp @@ -24,6 +24,7 @@ #include <sstream> #include "utils/functors.h" +#include "utils/logger.h" #include "dalstorage.h" #include "dalstoragesql.h" @@ -50,17 +51,19 @@ DALStorage::DALStorage() DALStorage::~DALStorage() throw() { - mDb->disconnect(); + if (mDb->isConnected()) { + close(); + } - // clean up loaded accounts. + // clean up accounts. for (Accounts::iterator it = mAccounts.begin(); it != mAccounts.end(); ++it) { - delete (*it); + delete it->first; } - // clean up loaded characters. + // clean up characters. for (Beings::iterator it = mCharacters.begin(); it != mCharacters.end(); ++it) @@ -71,24 +74,105 @@ DALStorage::~DALStorage() /** + * Connect to the database and initialize it if necessary. + */ +void +DALStorage::open(void) +{ + // do nothing if already connected. + if (mDb->isConnected()) { + return; + } + + using namespace dal; + + try { + // open a connection to the database. +#if defined (MYSQL_SUPPORT) || defined (POSTGRE_SUPPORT) + mDb->connect(getName(), getUser(), getPassword()); +#else // SQLITE_SUPPORT + // create the database file name. + std::string dbFile(getName()); + dbFile += ".db"; + mDb->connect(dbFile, "", ""); +#endif + + // ensure that the required tables are created. + // + // strategy1: find a way to obtain the list of tables from the + // underlying database and create the tables that are + // missing. + // + // strategy2: try to create the tables and check the exceptions + // thrown. + // + // comments: + // - strategy1 is easy to achieve if we are using MysQL as + // executing the request "show tables;" returns the list of + // tables. However, there is not such a query for SQLite3. + // When using SQLite3 from the interactive shell or the + // command line, the command ".tables" returns the list of + // tables but sqlite3_exec() does not validate this statement + // and fails. + // The cost of this strategy is: + // (num. tables to create + 1) queries at most and + // 1 at minimum. + // + // - strategy2 will work with probably most databases. + // The cost of this strategy is: + // (num. tables to create) queries. + + // we will stick with strategy2 for the moment as we are focusing + // on SQLite. + + createTable(MAPS_TBL_NAME, SQL_MAPS_TABLE); + createTable(ACCOUNTS_TBL_NAME, SQL_ACCOUNTS_TABLE); + createTable(CHARACTERS_TBL_NAME, SQL_CHARACTERS_TABLE); + createTable(ITEMS_TBL_NAME, SQL_ITEMS_TABLE); + createTable(WORLD_ITEMS_TBL_NAME, SQL_WORLD_ITEMS_TABLE); + createTable(INVENTORIES_TBL_NAME, SQL_INVENTORIES_TABLE); + } + catch (const DbConnectionFailure& e) { + LOG_ERROR("unable to connect to the database: " << e.what()) + } + catch (const DbSqlQueryExecFailure& e) { + LOG_ERROR("SQL query failure: " << e.what()) + } + + mIsOpen = mDb->isConnected(); +} + + +/** + * Disconnect from the database. + */ +void +DALStorage::close(void) +{ + mDb->disconnect(); + mIsOpen = mDb->isConnected(); +} + + +/** * Get an account by user name. */ Account* DALStorage::getAccount(const std::string& userName) { // connect to the database (if not connected yet). - connect(); + open(); // look for the account in the list first. Accounts::iterator it = std::find_if( mAccounts.begin(), mAccounts.end(), - std::bind2nd(obj_name_is<Account*>(), userName) + account_by_name(userName) ); if (it != mAccounts.end()) { - return (*it); + return it->first; } using namespace dal; @@ -116,7 +200,7 @@ DALStorage::getAccount(const std::string& userName) account->setEmail(accountInfo(0, 3)); // add the new Account to the list. - mAccounts.push_back(account); + mAccounts.insert(std::make_pair(account, AS_ACC_TO_UPDATE)); // load the characters associated with the account. sql = "select * from "; @@ -165,123 +249,98 @@ DALStorage::getAccount(const std::string& userName) * Add a new account. */ void -DALStorage::addAccount(const Account* account) +DALStorage::addAccount(Account* account) { - // TODO + if (account == 0) { + // maybe we should throw an exception instead + return; + } + + // mark this account as new so that the next flush will execute a SQL + // insert query instead of a SQL update query. + mAccounts.insert(std::make_pair(account, AS_NEW_ACCOUNT)); } /** - * Save changes to the database permanently. + * Delete an account. */ void -DALStorage::flush(void) +DALStorage::delAccount(const std::string& userName) { - // this feature is not currently provided by DAL. -} + // look for the account in memory first. + Accounts::iterator it = + std::find_if( + mAccounts.begin(), + mAccounts.end(), + account_by_name(userName) + ); + if (it != mAccounts.end()) { + switch (it->second) { + case AS_NEW_ACCOUNT: + // this is a newly added account and it has not even been + // saved into the database: remove it immediately. + delete it->first; + break; + + case AS_ACC_TO_UPDATE: + // change the status to AS_ACC_TO_DELETE so that it will be + // deleted at the next flush. + it->second = AS_ACC_TO_DELETE; + break; + + default: + break; + } -/** - * Get the number of Accounts saved in database. - */ -unsigned int -DALStorage::getAccountCount(void) -{ - // connect to the database (if not connected yet). - connect(); + // nothing else to do. + return; + } using namespace dal; - unsigned int value = 0; - try { - // query the database. - std::string sql("select count(*) from "); + std::string sql("select id from "); sql += ACCOUNTS_TBL_NAME; - sql += ";"; - const RecordSet& rs = mDb->execSql(sql); + sql += " where username = '"; + sql += userName; + sql += "';"; + const RecordSet& accountInfo = mDb->execSql(sql); - // specialize the string_to functor to convert - // a string to an unsigned int. - string_to<unsigned int> toUint; + // the account does not even exist in the database, + // there is nothing to do then. + if (accountInfo.isEmpty()) { + return; + } - value = toUint(rs(0, 0)); - } catch (const DbSqlQueryExecFailure& f) { - std::cout << "Get accounts count failed :'(" << std::endl; + // TODO: actually deleting the account from the database. + // order of deletion: + // 1. inventories of all the characters of the account, + // 2. all the characters, + // 3. the account itself. + } + catch (const DbSqlQueryExecFailure& e) { + // TODO: throw an exception. } - - return value; } /** - * Connect to the database and initialize it if necessary. + * Save changes to the database permanently. */ void -DALStorage::connect(void) +DALStorage::flush(void) { - // do nothing if already connected. - if (mDb->isConnected()) { - return; - } - - using namespace dal; - - try { - // open a connection to the database. - // TODO: get the database name, the user name and the user password - // from a configuration manager. - mDb->connect("tmw", "", ""); - - // ensure that the required tables are created. - // - // strategy1: find a way to obtain the list of tables from the - // underlying database and create the tables that are - // missing. - // - // strategy2: try to create the tables and check the exceptions - // thrown. - // - // comments: - // - strategy1 is easy to achieve if we are using MysQL as - // executing the request "show tables;" returns the list of - // tables. However, there is not such a query for SQLite3. - // When using SQLite3 from the interactive shell or the - // command line, the command ".tables" returns the list of - // tables but sqlite3_exec() does not validate this statement - // and fails. - // The cost of this strategy is: - // (num. tables to create + 1) queries at most and - // 1 at minimum. - // - // - strategy2 will work with probably most databases. - // The cost of this strategy is: - // (num. tables to create) queries. - - // we will stick with strategy2 for the moment as we are focusing - // on SQLite. - - createTable(MAPS_TBL_NAME, SQL_MAPS_TABLE); - createTable(ACCOUNTS_TBL_NAME, SQL_ACCOUNTS_TABLE); - createTable(CHARACTERS_TBL_NAME, SQL_CHARACTERS_TABLE); - createTable(ITEMS_TBL_NAME, SQL_ITEMS_TABLE); - createTable(WORLD_ITEMS_TBL_NAME, SQL_WORLD_ITEMS_TABLE); - createTable(INVENTORIES_TBL_NAME, SQL_INVENTORIES_TABLE); - - // Example data :) - mDb->execSql("insert into tmw_accounts values (0, 'nym', 'tHiSiSHaShEd', 'nym@test', 1, 0);"); - mDb->execSql("insert into tmw_accounts values (1, 'Bjorn', 'tHiSiSHaShEd', 'bjorn@test', 1, 0);"); - mDb->execSql("insert into tmw_accounts values (2, 'Usiu', 'tHiSiSHaShEd', 'usiu@test', 1, 0);"); - mDb->execSql("insert into tmw_accounts values (3, 'ElvenProgrammer', 'tHiSiSHaShEd', 'elven@test', 1, 0);"); - mDb->execSql("insert into tmw_characters values (0, 0, 'Nym the Great', 0, 99, 1000000, 0, 0, 'main.map', 1, 2, 3, 4, 5, 6);"); - } - catch (const DbConnectionFailure& e) { - std::cout << "unable to connect to the database: " - << e.what() << std::endl; - } - catch (const DbSqlQueryExecFailure& e) { - std::cout << e.what() << std::endl; - } + // TODO + // For each account in memory: + // - get the status + // - if AS_NEW_ACCOUNT then insert into database; + // - if AS_ACC_TO_UPDATE then update values from the database + // - if AS_ACC_TO_DELETE then delete from database + // Notes: + // - this will probably involve more than one table as the account may + // have characters associated to it. } diff --git a/src/dalstorage.h b/src/dalstorage.h index 59a3eb3b..78a689c7 100644 --- a/src/dalstorage.h +++ b/src/dalstorage.h @@ -51,6 +51,20 @@ class DALStorage: public Storage public: /** + * Connect to the database and initialize it if necessary. + */ + void + open(void); + + + /** + * Disconnect from the database. + */ + void + close(void); + + + /** * Get an account by user name. * * @param userName the owner of the account. @@ -67,23 +81,23 @@ class DALStorage: public Storage * @param account the new account. */ void - addAccount(const Account* account); + addAccount(Account* account); /** - * Save changes to the database permanently. + * Delete an account. + * + * @param userName the owner of the account. */ void - flush(void); + delAccount(const std::string& userName); /** - * Get the number of Accounts saved in database (test function). - * - * @return the number of Accounts. + * Save changes to the database permanently. */ - unsigned int - getAccountCount(void); + void + flush(void); private: @@ -114,13 +128,6 @@ class DALStorage: public Storage /** - * Connect to the database and initialize it if necessary. - */ - void - connect(void); - - - /** * Create the specified table. * * @param tblName the table name. @@ -135,9 +142,6 @@ class DALStorage: public Storage private: std::auto_ptr<dal::DataProvider> mDb; /**< the data provider */ - typedef std::vector<Account*> Accounts; - Accounts mAccounts; /**< the loaded accounts */ - Beings mCharacters; /**< the loaded characters */ }; diff --git a/src/main.cpp b/src/main.cpp index f5de932f..b8a493f6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -182,9 +182,7 @@ int main(int argc, char *argv[]) using namespace tmwserv; - Storage& store = Storage::instance(); - - LOG_INFO("Number of accounts on server: " << store.getAccountCount()) + Storage& store = Storage::instance("tmw"); // Test the database retrieval code LOG_INFO("Attempting to retrieve account with username 'nym'") diff --git a/src/storage.cpp b/src/storage.cpp index b5476426..adea90e6 100644 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -31,8 +31,11 @@ namespace tmwserv { -// initialize the static attribute. +// initialize the static attributes. Storage* Storage::mInstance = 0; +std::string Storage::mName(""); +std::string Storage::mUser(""); +std::string Storage::mPassword(""); /** @@ -59,10 +62,13 @@ Storage::~Storage(void) * Create an instance of Storage. */ Storage& -Storage::instance(void) +Storage::instance(const std::string& name) { if (mInstance == 0) { mInstance = new DALStorage(); + + // set the name of the storage. + mName = name; } // check that the instance has been created. @@ -82,7 +88,73 @@ Storage::destroy(void) { if (mInstance != 0) { delete mInstance; + mInstance = 0; } + + // reset the attributes. + mName = ""; + mUser = ""; + mPassword = ""; +} + + +/** + * Check if the storage is open. + */ +bool +Storage::isOpen(void) const +{ + return mIsOpen; +} + + +/** + * Get the storage name. + */ +const std::string& +Storage::getName(void) const +{ + return mName; +} + + +/** + * Set a user name for the storage. + */ +void +Storage::setUser(const std::string& userName) +{ + mUser = userName; +} + + +/** + * Get the user name. + */ +const std::string& +Storage::getUser(void) const +{ + return mUser; +} + + +/** + * Set a user password for the storage. + */ +void +Storage::setPassword(const std::string& password) +{ + mPassword = password; +} + + +/** + * Get the user password. + */ +const std::string& +Storage::getPassword(void) const +{ + return mPassword; } diff --git a/src/storage.h b/src/storage.h index e6c93ba7..5b695bb0 100644 --- a/src/storage.h +++ b/src/storage.h @@ -25,6 +25,8 @@ #define _TMWSERV_STORAGE_H_ +#include <map> + #include "object.h" #include "account.h" @@ -34,6 +36,83 @@ namespace tmwserv /** + * Enumeration type for the account status: + * + * AS_NEW_ACCOUNT : set to an account that is added to the storage and + * hence not yet save to disk (file or database); + * accounts with this status will be saved to disk at + * the next flush. + * AS_ACC_TO_UPDATE: set to an account that was loaded from disk; + * accounts with this status will be updated at the next + * flush. + * AS_ACC_TO_DELETE: set to an account that was loaded from disk; + * accounts with this status will be deleted from disk at + * the next flush. + * + * Notes: an account that is requested to be deleted and still has the + * status AS_NEW_ACCOUNT (and therefore not yet saved to disk) will + * be immediately deleted from memory to save useless transactions + * to disk. + */ +typedef enum { + AS_NEW_ACCOUNT, + AS_ACC_TO_UPDATE, + AS_ACC_TO_DELETE +} AccountStatus; + + +/** + * Functor to be used as the sorting criterion of the map defined below. + */ +struct account_sort_by_name + : public std::binary_function<Account*, Account*, bool> +{ + bool + operator()(Account* acc1, Account* acc2) const + { + return (acc1->getName() < acc2->getName()); + } +}; + + +/** + * Data type for the list of accounts. + */ +typedef std::map<Account*, AccountStatus, account_sort_by_name> Accounts; + + +/** + * Functor used to search an Account by name in the map defined above. + */ +class account_by_name +{ + public: + /** + * Constructor. + */ + account_by_name(const std::string& name) + : mName(name) + { + // NOP + } + + + /** + * Operator(). + */ + bool + operator()(std::pair<Account*, AccountStatus> elem) const + { + return (elem.first)->getName() == mName; + } + + + private: + std::string mName; /**< the name to look for */ +}; + + +/** * A storage to load and persist dynamic data. * * Notes: @@ -45,14 +124,25 @@ class Storage { public: /** - * Create an instance of Storage. + * Create a named instance of Storage. + * + * @param name the name of the storage. * * @return the unique instance of Storage. * * @exception std::bad_alloc if the instance cannot be created. + * + * Notes: + * - it is up to the underlying implementation of Storage to + * decide about what to do with the name, it could serve as the + * name of the database or the name of the file into which the + * storage will be dumped to. + * - the name of the storage is saved only when it's the first + * invocation of instance() or only when instance() is invoked + * after destroy(). */ static Storage& - instance(void); + instance(const std::string& name); /** @@ -63,6 +153,92 @@ class Storage /** + * Open the storage for read/write access. + * + * Depending on the underlying implementation of Storage, opening + * a storage would mean either opening a file or connecting to a + * database. + */ + virtual void + open(void) = 0; + + + /** + * Close the storage. + * + * Depending on the underlying implementation of Storage, closing + * a storage would mean either closing a file or disconnecting from + * a database. + */ + virtual void + close(void) = 0; + + + /** + * Check if the storage is open. + * + * @return true if the storage is open. + */ + bool + isOpen(void) const; + + + /** + * Get the storage name. + * + * @return the storage name. + */ + const std::string& + getName(void) const; + + + /** + * Set a user name for the storage. + * + * Depending on the underlying implementation of Storage, setting + * the user name may have no effect (e.g. DALStorage running on + * SQLite). + * + * @param userName the user name. + */ + void + setUser(const std::string& userName); + + + /** + * Get the user name. + * + * @return the user name (it may be an empty string if not set + * previously). + */ + const std::string& + getUser(void) const; + + + /** + * Set a user password for the storage. + * + * Depending on the underlying implementation of Storage, setting + * the user password may have no effect (e.g. DALStorage running on + * SQLite). + * + * @param password the user password. + */ + void + setPassword(const std::string& password); + + + /** + * Get the user password. + * + * @return the user password (it may be an empty string if not set + * previously). + */ + const std::string& + getPassword(void) const; + + + /** * Get an account by user name. * * @param userName the owner of the account. @@ -79,21 +255,23 @@ class Storage * @param account the new account. */ virtual void - addAccount(const Account* account) = 0; + addAccount(Account* account) = 0; /** - * Make sure any changes are saved. + * Delete an account. + * + * @param userName the owner of the account. */ virtual void - flush(void) = 0; + delAccount(const std::string& userName) = 0; /** - * Account count (test function). + * Saves the changes permanently. */ - virtual unsigned int - getAccountCount(void) = 0; + virtual void + flush(void) = 0; protected: @@ -125,8 +303,17 @@ class Storage operator=(const Storage& rhs); + protected: + Accounts mAccounts; /**< list of accounts in memory */ + Beings mCharacters; /**< the loaded characters */ + bool mIsOpen; /**< flag is true if the storage is open */ + + private: - static Storage* mInstance; /**< the unique instance of Storage */ + static Storage* mInstance; /**< the unique instance of Storage */ + static std::string mName; /**< the name of the storage */ + static std::string mUser; /**< the user name */ + static std::string mPassword; /**< the user password */ }; |