diff options
Diffstat (limited to 'src/database')
-rw-r--r-- | src/database/database-dummy.cpp | 38 | ||||
-rw-r--r-- | src/database/database-dummy.h | 9 | ||||
-rw-r--r-- | src/database/database-files.cpp | 125 | ||||
-rw-r--r-- | src/database/database-files.h | 26 | ||||
-rw-r--r-- | src/database/database-leveldb.cpp | 16 | ||||
-rw-r--r-- | src/database/database-postgresql.cpp | 41 | ||||
-rw-r--r-- | src/database/database-postgresql.h | 2 | ||||
-rw-r--r-- | src/database/database-redis.cpp | 6 | ||||
-rw-r--r-- | src/database/database-sqlite3.cpp | 135 | ||||
-rw-r--r-- | src/database/database-sqlite3.h | 25 | ||||
-rw-r--r-- | src/database/database.h | 13 |
11 files changed, 383 insertions, 53 deletions
diff --git a/src/database/database-dummy.cpp b/src/database/database-dummy.cpp index b56f341c5..629b2fb04 100644 --- a/src/database/database-dummy.cpp +++ b/src/database/database-dummy.cpp @@ -80,3 +80,41 @@ void Database_Dummy::listPlayers(std::vector<std::string> &res) res.emplace_back(player); } } + +bool Database_Dummy::getModEntries(const std::string &modname, StringMap *storage) +{ + const auto mod_pair = m_mod_meta_database.find(modname); + if (mod_pair != m_mod_meta_database.cend()) { + for (const auto &pair : mod_pair->second) { + (*storage)[pair.first] = pair.second; + } + } + return true; +} + +bool Database_Dummy::setModEntry(const std::string &modname, + const std::string &key, const std::string &value) +{ + auto mod_pair = m_mod_meta_database.find(modname); + if (mod_pair == m_mod_meta_database.end()) { + m_mod_meta_database[modname] = StringMap({{key, value}}); + } else { + mod_pair->second[key] = value; + } + return true; +} + +bool Database_Dummy::removeModEntry(const std::string &modname, const std::string &key) +{ + auto mod_pair = m_mod_meta_database.find(modname); + if (mod_pair != m_mod_meta_database.end()) + return mod_pair->second.erase(key) > 0; + return false; +} + +void Database_Dummy::listMods(std::vector<std::string> *res) +{ + for (const auto &pair : m_mod_meta_database) { + res->push_back(pair.first); + } +} diff --git a/src/database/database-dummy.h b/src/database/database-dummy.h index b69919f84..44b9e8d68 100644 --- a/src/database/database-dummy.h +++ b/src/database/database-dummy.h @@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "database.h" #include "irrlichttypes.h" -class Database_Dummy : public MapDatabase, public PlayerDatabase +class Database_Dummy : public MapDatabase, public PlayerDatabase, public ModMetadataDatabase { public: bool saveBlock(const v3s16 &pos, const std::string &data); @@ -37,10 +37,17 @@ public: bool removePlayer(const std::string &name); void listPlayers(std::vector<std::string> &res); + bool getModEntries(const std::string &modname, StringMap *storage); + bool setModEntry(const std::string &modname, + const std::string &key, const std::string &value); + bool removeModEntry(const std::string &modname, const std::string &key); + void listMods(std::vector<std::string> *res); + void beginSave() {} void endSave() {} private: std::map<s64, std::string> m_database; std::set<std::string> m_player_database; + std::unordered_map<std::string, StringMap> m_mod_meta_database; }; diff --git a/src/database/database-files.cpp b/src/database/database-files.cpp index d9d113b4e..7c0dbac28 100644 --- a/src/database/database-files.cpp +++ b/src/database/database-files.cpp @@ -18,7 +18,6 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include <cassert> -#include <json/json.h> #include "convert_json.h" #include "database-files.h" #include "remoteplayer.h" @@ -376,3 +375,127 @@ bool AuthDatabaseFiles::writeAuthFile() } return true; } + +ModMetadataDatabaseFiles::ModMetadataDatabaseFiles(const std::string &savedir): + m_storage_dir(savedir + DIR_DELIM + "mod_storage") +{ +} + +bool ModMetadataDatabaseFiles::getModEntries(const std::string &modname, StringMap *storage) +{ + Json::Value *meta = getOrCreateJson(modname); + if (!meta) + return false; + + const Json::Value::Members attr_list = meta->getMemberNames(); + for (const auto &it : attr_list) { + Json::Value attr_value = (*meta)[it]; + (*storage)[it] = attr_value.asString(); + } + + return true; +} + +bool ModMetadataDatabaseFiles::setModEntry(const std::string &modname, + const std::string &key, const std::string &value) +{ + Json::Value *meta = getOrCreateJson(modname); + if (!meta) + return false; + + (*meta)[key] = Json::Value(value); + m_modified.insert(modname); + + return true; +} + +bool ModMetadataDatabaseFiles::removeModEntry(const std::string &modname, + const std::string &key) +{ + Json::Value *meta = getOrCreateJson(modname); + if (!meta) + return false; + + Json::Value removed; + if (meta->removeMember(key, &removed)) { + m_modified.insert(modname); + return true; + } + return false; +} + +void ModMetadataDatabaseFiles::beginSave() +{ +} + +void ModMetadataDatabaseFiles::endSave() +{ + if (m_modified.empty()) + return; + + if (!fs::CreateAllDirs(m_storage_dir)) { + errorstream << "ModMetadataDatabaseFiles: Unable to save. '" + << m_storage_dir << "' cannot be created." << std::endl; + return; + } + if (!fs::IsDir(m_storage_dir)) { + errorstream << "ModMetadataDatabaseFiles: Unable to save. '" + << m_storage_dir << "' is not a directory." << std::endl; + return; + } + + for (auto it = m_modified.begin(); it != m_modified.end();) { + const std::string &modname = *it; + + const Json::Value &json = m_mod_meta[modname]; + + if (!fs::safeWriteToFile(m_storage_dir + DIR_DELIM + modname, fastWriteJson(json))) { + errorstream << "ModMetadataDatabaseFiles[" << modname + << "]: failed to write file." << std::endl; + ++it; + continue; + } + + it = m_modified.erase(it); + } +} + +void ModMetadataDatabaseFiles::listMods(std::vector<std::string> *res) +{ + // List in-memory metadata first. + for (const auto &pair : m_mod_meta) { + res->push_back(pair.first); + } + + // List other metadata present in the filesystem. + for (const auto &entry : fs::GetDirListing(m_storage_dir)) { + if (!entry.dir && m_mod_meta.count(entry.name) == 0) + res->push_back(entry.name); + } +} + +Json::Value *ModMetadataDatabaseFiles::getOrCreateJson(const std::string &modname) +{ + auto found = m_mod_meta.find(modname); + if (found != m_mod_meta.end()) + return &found->second; + + Json::Value meta(Json::objectValue); + + std::string path = m_storage_dir + DIR_DELIM + modname; + if (fs::PathExists(path)) { + std::ifstream is(path.c_str(), std::ios_base::binary); + + Json::CharReaderBuilder builder; + builder.settings_["collectComments"] = false; + std::string errs; + + if (!Json::parseFromStream(builder, is, &meta, &errs)) { + errorstream << "ModMetadataDatabaseFiles[" << modname + << "]: failed to decode data: " << errs << std::endl; + return nullptr; + } + } + + return &(m_mod_meta[modname] = meta); +} diff --git a/src/database/database-files.h b/src/database/database-files.h index e647a2e24..962e4d7bb 100644 --- a/src/database/database-files.h +++ b/src/database/database-files.h @@ -25,6 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "database.h" #include <unordered_map> +#include <unordered_set> +#include <json/json.h> class PlayerDatabaseFiles : public PlayerDatabase { @@ -69,3 +71,27 @@ private: bool readAuthFile(); bool writeAuthFile(); }; + +class ModMetadataDatabaseFiles : public ModMetadataDatabase +{ +public: + ModMetadataDatabaseFiles(const std::string &savedir); + virtual ~ModMetadataDatabaseFiles() = default; + + virtual bool getModEntries(const std::string &modname, StringMap *storage); + virtual bool setModEntry(const std::string &modname, + const std::string &key, const std::string &value); + virtual bool removeModEntry(const std::string &modname, const std::string &key); + virtual void listMods(std::vector<std::string> *res); + + virtual void beginSave(); + virtual void endSave(); + +private: + Json::Value *getOrCreateJson(const std::string &modname); + bool writeJson(const std::string &modname, const Json::Value &json); + + std::string m_storage_dir; + std::unordered_map<std::string, Json::Value> m_mod_meta; + std::unordered_set<std::string> m_modified; +}; diff --git a/src/database/database-leveldb.cpp b/src/database/database-leveldb.cpp index 73cd63f6d..39f4c8442 100644 --- a/src/database/database-leveldb.cpp +++ b/src/database/database-leveldb.cpp @@ -70,11 +70,11 @@ bool Database_LevelDB::saveBlock(const v3s16 &pos, const std::string &data) void Database_LevelDB::loadBlock(const v3s16 &pos, std::string *block) { - std::string datastr; leveldb::Status status = m_database->Get(leveldb::ReadOptions(), - i64tos(getBlockAsInteger(pos)), &datastr); + i64tos(getBlockAsInteger(pos)), block); - *block = (status.ok()) ? datastr : ""; + if (!status.ok()) + block->clear(); } bool Database_LevelDB::deleteBlock(const v3s16 &pos) @@ -131,7 +131,7 @@ void PlayerDatabaseLevelDB::savePlayer(RemotePlayer *player) std::string (long) serialized_inventory */ - std::ostringstream os; + std::ostringstream os(std::ios_base::binary); writeU8(os, 1); PlayerSAO *sao = player->getPlayerSAO(); @@ -142,7 +142,7 @@ void PlayerDatabaseLevelDB::savePlayer(RemotePlayer *player) writeF32(os, sao->getRotation().Y); writeU16(os, sao->getBreath()); - StringMap stringvars = sao->getMeta().getStrings(); + const auto &stringvars = sao->getMeta().getStrings(); writeU32(os, stringvars.size()); for (const auto &it : stringvars) { os << serializeString16(it.first); @@ -170,7 +170,7 @@ bool PlayerDatabaseLevelDB::loadPlayer(RemotePlayer *player, PlayerSAO *sao) player->getName(), &raw); if (!s.ok()) return false; - std::istringstream is(raw); + std::istringstream is(raw, std::ios_base::binary); if (readU8(is) > 1) return false; @@ -230,7 +230,7 @@ bool AuthDatabaseLevelDB::getAuth(const std::string &name, AuthEntry &res) leveldb::Status s = m_database->Get(leveldb::ReadOptions(), name, &raw); if (!s.ok()) return false; - std::istringstream is(raw); + std::istringstream is(raw, std::ios_base::binary); /* u8 version = 1 @@ -262,7 +262,7 @@ bool AuthDatabaseLevelDB::getAuth(const std::string &name, AuthEntry &res) bool AuthDatabaseLevelDB::saveAuth(const AuthEntry &authEntry) { - std::ostringstream os; + std::ostringstream os(std::ios_base::binary); writeU8(os, 1); os << serializeString16(authEntry.password); diff --git a/src/database/database-postgresql.cpp b/src/database/database-postgresql.cpp index e1bb39928..3469f4242 100644 --- a/src/database/database-postgresql.cpp +++ b/src/database/database-postgresql.cpp @@ -39,20 +39,24 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "remoteplayer.h" #include "server/player_sao.h" -Database_PostgreSQL::Database_PostgreSQL(const std::string &connect_string) : +Database_PostgreSQL::Database_PostgreSQL(const std::string &connect_string, + const char *type) : m_connect_string(connect_string) { if (m_connect_string.empty()) { - throw SettingNotFoundException( - "Set pgsql_connection string in world.mt to " + // Use given type to reference the exact setting in the error message + std::string s = type; + std::string msg = + "Set pgsql" + s + "_connection string in world.mt to " "use the postgresql backend\n" "Notes:\n" - "pgsql_connection has the following form: \n" - "\tpgsql_connection = host=127.0.0.1 port=5432 user=mt_user " - "password=mt_password dbname=minetest_world\n" + "pgsql" + s + "_connection has the following form: \n" + "\tpgsql" + s + "_connection = host=127.0.0.1 port=5432 " + "user=mt_user password=mt_password dbname=minetest" + s + "\n" "mt_user should have CREATE TABLE, INSERT, SELECT, UPDATE and " - "DELETE rights on the database.\n" - "Don't create mt_user as a SUPERUSER!"); + "DELETE rights on the database. " + "Don't create mt_user as a SUPERUSER!"; + throw SettingNotFoundException(msg); } } @@ -166,7 +170,7 @@ void Database_PostgreSQL::rollback() } MapDatabasePostgreSQL::MapDatabasePostgreSQL(const std::string &connect_string): - Database_PostgreSQL(connect_string), + Database_PostgreSQL(connect_string, ""), MapDatabase() { connectToDatabase(); @@ -270,10 +274,10 @@ void MapDatabasePostgreSQL::loadBlock(const v3s16 &pos, std::string *block) PGresult *results = execPrepared("read_block", ARRLEN(args), args, argLen, argFmt, false); - *block = ""; - if (PQntuples(results)) - *block = std::string(PQgetvalue(results, 0, 0), PQgetlength(results, 0, 0)); + block->assign(PQgetvalue(results, 0, 0), PQgetlength(results, 0, 0)); + else + block->clear(); PQclear(results); } @@ -315,7 +319,7 @@ void MapDatabasePostgreSQL::listAllLoadableBlocks(std::vector<v3s16> &dst) * Player Database */ PlayerDatabasePostgreSQL::PlayerDatabasePostgreSQL(const std::string &connect_string): - Database_PostgreSQL(connect_string), + Database_PostgreSQL(connect_string, "_player"), PlayerDatabase() { connectToDatabase(); @@ -492,6 +496,7 @@ void PlayerDatabasePostgreSQL::savePlayer(RemotePlayer *player) execPrepared("remove_player_inventory_items", 1, rmvalues); std::vector<const InventoryList*> inventory_lists = sao->getInventory()->getLists(); + std::ostringstream oss; for (u16 i = 0; i < inventory_lists.size(); i++) { const InventoryList* list = inventory_lists[i]; const std::string &name = list->getName(); @@ -508,9 +513,10 @@ void PlayerDatabasePostgreSQL::savePlayer(RemotePlayer *player) execPrepared("add_player_inventory", 5, inv_values); for (u32 j = 0; j < list->getSize(); j++) { - std::ostringstream os; - list->getItem(j).serialize(os); - std::string itemStr = os.str(), slotId = itos(j); + oss.str(""); + oss.clear(); + list->getItem(j).serialize(oss); + std::string itemStr = oss.str(), slotId = itos(j); const char* invitem_values[] = { player->getName(), @@ -637,7 +643,8 @@ void PlayerDatabasePostgreSQL::listPlayers(std::vector<std::string> &res) } AuthDatabasePostgreSQL::AuthDatabasePostgreSQL(const std::string &connect_string) : - Database_PostgreSQL(connect_string), AuthDatabase() + Database_PostgreSQL(connect_string, "_auth"), + AuthDatabase() { connectToDatabase(); } diff --git a/src/database/database-postgresql.h b/src/database/database-postgresql.h index f47deda33..81b4a2b10 100644 --- a/src/database/database-postgresql.h +++ b/src/database/database-postgresql.h @@ -29,7 +29,7 @@ class Settings; class Database_PostgreSQL: public Database { public: - Database_PostgreSQL(const std::string &connect_string); + Database_PostgreSQL(const std::string &connect_string, const char *type); ~Database_PostgreSQL(); void beginSave(); diff --git a/src/database/database-redis.cpp b/src/database/database-redis.cpp index 096ea504d..5ffff67b7 100644 --- a/src/database/database-redis.cpp +++ b/src/database/database-redis.cpp @@ -127,8 +127,7 @@ void Database_Redis::loadBlock(const v3s16 &pos, std::string *block) switch (reply->type) { case REDIS_REPLY_STRING: { - *block = std::string(reply->str, reply->len); - // std::string copies the memory so this won't cause any problems + block->assign(reply->str, reply->len); freeReplyObject(reply); return; } @@ -141,8 +140,7 @@ void Database_Redis::loadBlock(const v3s16 &pos, std::string *block) "Redis command 'HGET %s %s' errored: ") + errstr); } case REDIS_REPLY_NIL: { - *block = ""; - // block not found in database + block->clear(); freeReplyObject(reply); return; } diff --git a/src/database/database-sqlite3.cpp b/src/database/database-sqlite3.cpp index 4560743b9..1e63ae9d8 100644 --- a/src/database/database-sqlite3.cpp +++ b/src/database/database-sqlite3.cpp @@ -228,11 +228,7 @@ void MapDatabaseSQLite3::createDatabase() void MapDatabaseSQLite3::initStatements() { PREPARE_STATEMENT(read, "SELECT `data` FROM `blocks` WHERE `pos` = ? LIMIT 1"); -#ifdef __ANDROID__ - PREPARE_STATEMENT(write, "INSERT INTO `blocks` (`pos`, `data`) VALUES (?, ?)"); -#else PREPARE_STATEMENT(write, "REPLACE INTO `blocks` (`pos`, `data`) VALUES (?, ?)"); -#endif PREPARE_STATEMENT(delete, "DELETE FROM `blocks` WHERE `pos` = ?"); PREPARE_STATEMENT(list, "SELECT `pos` FROM `blocks`"); @@ -265,19 +261,6 @@ bool MapDatabaseSQLite3::saveBlock(const v3s16 &pos, const std::string &data) { verifyDatabase(); -#ifdef __ANDROID__ - /** - * Note: For some unknown reason SQLite3 fails to REPLACE blocks on Android, - * deleting them and then inserting works. - */ - bindPos(m_stmt_read, pos); - - if (sqlite3_step(m_stmt_read) == SQLITE_ROW) { - deleteBlock(pos); - } - sqlite3_reset(m_stmt_read); -#endif - bindPos(m_stmt_write, pos); SQLOK(sqlite3_bind_blob(m_stmt_write, 2, data.data(), data.size(), NULL), "Internal error: failed to bind query at " __FILE__ ":" TOSTRING(__LINE__)); @@ -302,7 +285,10 @@ void MapDatabaseSQLite3::loadBlock(const v3s16 &pos, std::string *block) const char *data = (const char *) sqlite3_column_blob(m_stmt_read, 0); size_t len = sqlite3_column_bytes(m_stmt_read, 0); - *block = (data) ? std::string(data, len) : ""; + if (data) + block->assign(data, len); + else + block->clear(); sqlite3_step(m_stmt_read); // We should never get more than 1 row, so ok to reset @@ -491,6 +477,7 @@ void PlayerDatabaseSQLite3::savePlayer(RemotePlayer *player) sqlite3_reset(m_stmt_player_remove_inventory_items); std::vector<const InventoryList*> inventory_lists = sao->getInventory()->getLists(); + std::ostringstream oss; for (u16 i = 0; i < inventory_lists.size(); i++) { const InventoryList* list = inventory_lists[i]; @@ -503,9 +490,10 @@ void PlayerDatabaseSQLite3::savePlayer(RemotePlayer *player) sqlite3_reset(m_stmt_player_add_inventory); for (u32 j = 0; j < list->getSize(); j++) { - std::ostringstream os; - list->getItem(j).serialize(os); - std::string itemStr = os.str(); + oss.str(""); + oss.clear(); + list->getItem(j).serialize(oss); + std::string itemStr = oss.str(); str_to_sqlite(m_stmt_player_add_inventory_items, 1, player->getName()); int_to_sqlite(m_stmt_player_add_inventory_items, 2, i); @@ -774,3 +762,108 @@ void AuthDatabaseSQLite3::writePrivileges(const AuthEntry &authEntry) sqlite3_reset(m_stmt_write_privs); } } + +ModMetadataDatabaseSQLite3::ModMetadataDatabaseSQLite3(const std::string &savedir): + Database_SQLite3(savedir, "mod_storage"), ModMetadataDatabase() +{ +} + +ModMetadataDatabaseSQLite3::~ModMetadataDatabaseSQLite3() +{ + FINALIZE_STATEMENT(m_stmt_remove) + FINALIZE_STATEMENT(m_stmt_set) + FINALIZE_STATEMENT(m_stmt_get) +} + +void ModMetadataDatabaseSQLite3::createDatabase() +{ + assert(m_database); // Pre-condition + + SQLOK(sqlite3_exec(m_database, + "CREATE TABLE IF NOT EXISTS `entries` (\n" + " `modname` TEXT NOT NULL,\n" + " `key` BLOB NOT NULL,\n" + " `value` BLOB NOT NULL,\n" + " PRIMARY KEY (`modname`, `key`)\n" + ");\n", + NULL, NULL, NULL), + "Failed to create database table"); +} + +void ModMetadataDatabaseSQLite3::initStatements() +{ + PREPARE_STATEMENT(get, "SELECT `key`, `value` FROM `entries` WHERE `modname` = ?"); + PREPARE_STATEMENT(set, + "REPLACE INTO `entries` (`modname`, `key`, `value`) VALUES (?, ?, ?)"); + PREPARE_STATEMENT(remove, "DELETE FROM `entries` WHERE `modname` = ? AND `key` = ?"); +} + +bool ModMetadataDatabaseSQLite3::getModEntries(const std::string &modname, StringMap *storage) +{ + verifyDatabase(); + + str_to_sqlite(m_stmt_get, 1, modname); + while (sqlite3_step(m_stmt_get) == SQLITE_ROW) { + const char *key_data = (const char *) sqlite3_column_blob(m_stmt_get, 0); + size_t key_len = sqlite3_column_bytes(m_stmt_get, 0); + const char *value_data = (const char *) sqlite3_column_blob(m_stmt_get, 1); + size_t value_len = sqlite3_column_bytes(m_stmt_get, 1); + (*storage)[std::string(key_data, key_len)] = std::string(value_data, value_len); + } + sqlite3_vrfy(sqlite3_errcode(m_database), SQLITE_DONE); + + sqlite3_reset(m_stmt_get); + + return true; +} + +bool ModMetadataDatabaseSQLite3::setModEntry(const std::string &modname, + const std::string &key, const std::string &value) +{ + verifyDatabase(); + + str_to_sqlite(m_stmt_set, 1, modname); + SQLOK(sqlite3_bind_blob(m_stmt_set, 2, key.data(), key.size(), NULL), + "Internal error: failed to bind query at " __FILE__ ":" TOSTRING(__LINE__)); + SQLOK(sqlite3_bind_blob(m_stmt_set, 3, value.data(), value.size(), NULL), + "Internal error: failed to bind query at " __FILE__ ":" TOSTRING(__LINE__)); + SQLRES(sqlite3_step(m_stmt_set), SQLITE_DONE, "Failed to set mod entry") + + sqlite3_reset(m_stmt_set); + + return true; +} + +bool ModMetadataDatabaseSQLite3::removeModEntry(const std::string &modname, + const std::string &key) +{ + verifyDatabase(); + + str_to_sqlite(m_stmt_remove, 1, modname); + SQLOK(sqlite3_bind_blob(m_stmt_remove, 2, key.data(), key.size(), NULL), + "Internal error: failed to bind query at " __FILE__ ":" TOSTRING(__LINE__)); + sqlite3_vrfy(sqlite3_step(m_stmt_remove), SQLITE_DONE); + int changes = sqlite3_changes(m_database); + + sqlite3_reset(m_stmt_remove); + + return changes > 0; +} + +void ModMetadataDatabaseSQLite3::listMods(std::vector<std::string> *res) +{ + verifyDatabase(); + + char *errmsg; + int status = sqlite3_exec(m_database, + "SELECT `modname` FROM `entries` GROUP BY `modname`;", + [](void *res_vp, int n_col, char **cols, char **col_names) -> int { + ((decltype(res)) res_vp)->emplace_back(cols[0]); + return 0; + }, (void *) res, &errmsg); + if (status != SQLITE_OK) { + DatabaseException e(std::string("Error trying to list mods with metadata: ") + errmsg); + sqlite3_free(errmsg); + throw e; + } +} diff --git a/src/database/database-sqlite3.h b/src/database/database-sqlite3.h index d7202a918..5e3d7c96c 100644 --- a/src/database/database-sqlite3.h +++ b/src/database/database-sqlite3.h @@ -232,3 +232,28 @@ private: sqlite3_stmt *m_stmt_delete_privs = nullptr; sqlite3_stmt *m_stmt_last_insert_rowid = nullptr; }; + +class ModMetadataDatabaseSQLite3 : private Database_SQLite3, public ModMetadataDatabase +{ +public: + ModMetadataDatabaseSQLite3(const std::string &savedir); + virtual ~ModMetadataDatabaseSQLite3(); + + virtual bool getModEntries(const std::string &modname, StringMap *storage); + virtual bool setModEntry(const std::string &modname, + const std::string &key, const std::string &value); + virtual bool removeModEntry(const std::string &modname, const std::string &key); + virtual void listMods(std::vector<std::string> *res); + + virtual void beginSave() { Database_SQLite3::beginSave(); } + virtual void endSave() { Database_SQLite3::endSave(); } + +protected: + virtual void createDatabase(); + virtual void initStatements(); + +private: + sqlite3_stmt *m_stmt_get = nullptr; + sqlite3_stmt *m_stmt_set = nullptr; + sqlite3_stmt *m_stmt_remove = nullptr; +}; diff --git a/src/database/database.h b/src/database/database.h index b7d551935..fbb5befea 100644 --- a/src/database/database.h +++ b/src/database/database.h @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irr_v3d.h" #include "irrlichttypes.h" #include "util/basic_macros.h" +#include "util/string.h" class Database { @@ -84,3 +85,15 @@ public: virtual void listNames(std::vector<std::string> &res) = 0; virtual void reload() = 0; }; + +class ModMetadataDatabase : public Database +{ +public: + virtual ~ModMetadataDatabase() = default; + + virtual bool getModEntries(const std::string &modname, StringMap *storage) = 0; + virtual bool setModEntry(const std::string &modname, + const std::string &key, const std::string &value) = 0; + virtual bool removeModEntry(const std::string &modname, const std::string &key) = 0; + virtual void listMods(std::vector<std::string> *res) = 0; +}; |