mirror of
https://github.com/minetest/minetest.git
synced 2024-11-27 01:53:45 +01:00
Add mod storage PostgreSQL backend
This commit is contained in:
parent
9dbac989bd
commit
aaa05f901a
@ -38,6 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "settings.h"
|
||||
#include "remoteplayer.h"
|
||||
#include "server/player_sao.h"
|
||||
#include <cstdlib>
|
||||
|
||||
Database_PostgreSQL::Database_PostgreSQL(const std::string &connect_string,
|
||||
const char *type) :
|
||||
@ -812,5 +813,211 @@ void AuthDatabasePostgreSQL::writePrivileges(const AuthEntry &authEntry)
|
||||
}
|
||||
}
|
||||
|
||||
ModMetadataDatabasePostgreSQL::ModMetadataDatabasePostgreSQL(const std::string &connect_string):
|
||||
Database_PostgreSQL(connect_string, "_mod_storage"),
|
||||
ModMetadataDatabase()
|
||||
{
|
||||
connectToDatabase();
|
||||
}
|
||||
|
||||
void ModMetadataDatabasePostgreSQL::createDatabase()
|
||||
{
|
||||
createTableIfNotExists("mod_storage",
|
||||
"CREATE TABLE mod_storage ("
|
||||
"modname TEXT NOT NULL,"
|
||||
"key BYTEA NOT NULL,"
|
||||
"value BYTEA NOT NULL,"
|
||||
"PRIMARY KEY (modname, key)"
|
||||
");");
|
||||
|
||||
infostream << "PostgreSQL: Mod Storage Database was initialized." << std::endl;
|
||||
}
|
||||
|
||||
void ModMetadataDatabasePostgreSQL::initStatements()
|
||||
{
|
||||
prepareStatement("get_all",
|
||||
"SELECT key, value FROM mod_storage WHERE modname = $1");
|
||||
prepareStatement("get_all_keys",
|
||||
"SELECT key FROM mod_storage WHERE modname = $1");
|
||||
prepareStatement("get",
|
||||
"SELECT value FROM mod_storage WHERE modname = $1 AND key = $2::bytea");
|
||||
prepareStatement("has",
|
||||
"SELECT true FROM mod_storage WHERE modname = $1 AND key = $2::bytea");
|
||||
if (getPGVersion() < 90500) {
|
||||
prepareStatement("set_insert",
|
||||
"INSERT INTO mod_storage (modname, key, value) "
|
||||
"SELECT $1, $2::bytea, $3::bytea "
|
||||
"WHERE NOT EXISTS ("
|
||||
"SELECT true FROM mod_storage WHERE modname = $1 AND key = $2::bytea"
|
||||
")");
|
||||
prepareStatement("set_update",
|
||||
"UPDATE mod_storage SET value = $3::bytea WHERE modname = $1 AND key = $2::bytea");
|
||||
} else {
|
||||
prepareStatement("set",
|
||||
"INSERT INTO mod_storage (modname, key, value) VALUES ($1, $2::bytea, $3::bytea) "
|
||||
"ON CONFLICT ON CONSTRAINT mod_storage_pkey DO "
|
||||
"UPDATE SET value = $3::bytea");
|
||||
}
|
||||
prepareStatement("remove",
|
||||
"DELETE FROM mod_storage WHERE modname = $1 AND key = $2::bytea");
|
||||
prepareStatement("remove_all",
|
||||
"DELETE FROM mod_storage WHERE modname = $1");
|
||||
prepareStatement("list",
|
||||
"SELECT DISTINCT modname FROM mod_storage");
|
||||
}
|
||||
|
||||
bool ModMetadataDatabasePostgreSQL::getModEntries(const std::string &modname, StringMap *storage)
|
||||
{
|
||||
verifyDatabase();
|
||||
|
||||
const void *args[] = { modname.c_str() };
|
||||
const int argLen[] = { -1 };
|
||||
const int argFmt[] = { 0 };
|
||||
PGresult *results = execPrepared("get_all", ARRLEN(args),
|
||||
args, argLen, argFmt, false);
|
||||
|
||||
int numrows = PQntuples(results);
|
||||
|
||||
for (int row = 0; row < numrows; ++row) {
|
||||
std::string key(PQgetvalue(results, row, 0), PQgetlength(results, row, 0));
|
||||
std::string value(PQgetvalue(results, row, 1), PQgetlength(results, row, 1));
|
||||
storage->emplace(std::move(key), std::move(value));
|
||||
}
|
||||
|
||||
PQclear(results);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModMetadataDatabasePostgreSQL::getModKeys(const std::string &modname,
|
||||
std::vector<std::string> *storage)
|
||||
{
|
||||
verifyDatabase();
|
||||
|
||||
const void *args[] = { modname.c_str() };
|
||||
const int argLen[] = { -1 };
|
||||
const int argFmt[] = { 0 };
|
||||
PGresult *results = execPrepared("get_all_keys", ARRLEN(args),
|
||||
args, argLen, argFmt, false);
|
||||
|
||||
int numrows = PQntuples(results);
|
||||
|
||||
storage->reserve(storage->size() + numrows);
|
||||
for (int row = 0; row < numrows; ++row)
|
||||
storage->emplace_back(PQgetvalue(results, row, 0), PQgetlength(results, row, 0));
|
||||
|
||||
PQclear(results);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModMetadataDatabasePostgreSQL::getModEntry(const std::string &modname,
|
||||
const std::string &key, std::string *value)
|
||||
{
|
||||
verifyDatabase();
|
||||
|
||||
const void *args[] = { modname.c_str(), key.c_str() };
|
||||
const int argLen[] = { -1, (int)MYMIN(key.size(), INT_MAX) };
|
||||
const int argFmt[] = { 0, 1 };
|
||||
PGresult *results = execPrepared("get", ARRLEN(args), args, argLen, argFmt, false);
|
||||
|
||||
int numrows = PQntuples(results);
|
||||
bool found = numrows > 0;
|
||||
|
||||
if (found)
|
||||
value->assign(PQgetvalue(results, 0, 0), PQgetlength(results, 0, 0));
|
||||
|
||||
PQclear(results);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
bool ModMetadataDatabasePostgreSQL::hasModEntry(const std::string &modname,
|
||||
const std::string &key)
|
||||
{
|
||||
verifyDatabase();
|
||||
|
||||
const void *args[] = { modname.c_str(), key.c_str() };
|
||||
const int argLen[] = { -1, (int)MYMIN(key.size(), INT_MAX) };
|
||||
const int argFmt[] = { 0, 1 };
|
||||
PGresult *results = execPrepared("has", ARRLEN(args), args, argLen, argFmt, false);
|
||||
|
||||
int numrows = PQntuples(results);
|
||||
bool found = numrows > 0;
|
||||
|
||||
PQclear(results);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
bool ModMetadataDatabasePostgreSQL::setModEntry(const std::string &modname,
|
||||
const std::string &key, const std::string &value)
|
||||
{
|
||||
verifyDatabase();
|
||||
|
||||
const void *args[] = { modname.c_str(), key.c_str(), value.c_str() };
|
||||
const int argLen[] = {
|
||||
-1,
|
||||
(int)MYMIN(key.size(), INT_MAX),
|
||||
(int)MYMIN(value.size(), INT_MAX),
|
||||
};
|
||||
const int argFmt[] = { 0, 1, 1 };
|
||||
if (getPGVersion() < 90500) {
|
||||
execPrepared("set_insert", ARRLEN(args), args, argLen, argFmt);
|
||||
execPrepared("set_update", ARRLEN(args), args, argLen, argFmt);
|
||||
} else {
|
||||
execPrepared("set", ARRLEN(args), args, argLen, argFmt);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModMetadataDatabasePostgreSQL::removeModEntry(const std::string &modname,
|
||||
const std::string &key)
|
||||
{
|
||||
verifyDatabase();
|
||||
|
||||
const void *args[] = { modname.c_str(), key.c_str() };
|
||||
const int argLen[] = { -1, (int)MYMIN(key.size(), INT_MAX) };
|
||||
const int argFmt[] = { 0, 1 };
|
||||
PGresult *results = execPrepared("remove", ARRLEN(args), args, argLen, argFmt, false);
|
||||
|
||||
int affected = atoi(PQcmdTuples(results));
|
||||
|
||||
PQclear(results);
|
||||
|
||||
return affected > 0;
|
||||
}
|
||||
|
||||
bool ModMetadataDatabasePostgreSQL::removeModEntries(const std::string &modname)
|
||||
{
|
||||
verifyDatabase();
|
||||
|
||||
const void *args[] = { modname.c_str() };
|
||||
const int argLen[] = { -1 };
|
||||
const int argFmt[] = { 0 };
|
||||
PGresult *results = execPrepared("remove_all", ARRLEN(args), args, argLen, argFmt, false);
|
||||
|
||||
int affected = atoi(PQcmdTuples(results));
|
||||
|
||||
PQclear(results);
|
||||
|
||||
return affected > 0;
|
||||
}
|
||||
|
||||
void ModMetadataDatabasePostgreSQL::listMods(std::vector<std::string> *res)
|
||||
{
|
||||
verifyDatabase();
|
||||
|
||||
PGresult *results = execPrepared("list", 0, NULL, false);
|
||||
|
||||
int numrows = PQntuples(results);
|
||||
|
||||
for (int row = 0; row < numrows; ++row)
|
||||
res->emplace_back(PQgetvalue(results, row, 0), PQgetlength(results, row, 0));
|
||||
|
||||
PQclear(results);
|
||||
}
|
||||
|
||||
|
||||
#endif // USE_POSTGRESQL
|
||||
|
@ -169,3 +169,27 @@ protected:
|
||||
private:
|
||||
virtual void writePrivileges(const AuthEntry &authEntry);
|
||||
};
|
||||
|
||||
class ModMetadataDatabasePostgreSQL : private Database_PostgreSQL, public ModMetadataDatabase
|
||||
{
|
||||
public:
|
||||
ModMetadataDatabasePostgreSQL(const std::string &connect_string);
|
||||
~ModMetadataDatabasePostgreSQL() = default;
|
||||
|
||||
bool getModEntries(const std::string &modname, StringMap *storage);
|
||||
bool getModKeys(const std::string &modname, std::vector<std::string> *storage);
|
||||
bool getModEntry(const std::string &modname, const std::string &key, std::string *value);
|
||||
bool hasModEntry(const std::string &modname, const std::string &key);
|
||||
bool setModEntry(const std::string &modname,
|
||||
const std::string &key, const std::string &value);
|
||||
bool removeModEntry(const std::string &modname, const std::string &key);
|
||||
bool removeModEntries(const std::string &modname);
|
||||
void listMods(std::vector<std::string> *res);
|
||||
|
||||
void beginSave() { Database_PostgreSQL::beginSave(); }
|
||||
void endSave() { Database_PostgreSQL::endSave(); }
|
||||
|
||||
protected:
|
||||
virtual void createDatabase();
|
||||
virtual void initStatements();
|
||||
};
|
||||
|
@ -67,6 +67,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "server/serverinventorymgr.h"
|
||||
#include "translation.h"
|
||||
#include "database/database-sqlite3.h"
|
||||
#if USE_POSTGRESQL
|
||||
#include "database/database-postgresql.h"
|
||||
#endif
|
||||
#include "database/database-files.h"
|
||||
#include "database/database-dummy.h"
|
||||
#include "gameparams.h"
|
||||
@ -4025,6 +4028,14 @@ ModMetadataDatabase *Server::openModStorageDatabase(const std::string &backend,
|
||||
if (backend == "sqlite3")
|
||||
return new ModMetadataDatabaseSQLite3(world_path);
|
||||
|
||||
#if USE_POSTGRESQL
|
||||
if (backend == "postgresql") {
|
||||
std::string connect_string;
|
||||
world_mt.getNoEx("pgsql_mod_storage_connection", connect_string);
|
||||
return new ModMetadataDatabasePostgreSQL(connect_string);
|
||||
}
|
||||
#endif // USE_POSTGRESQL
|
||||
|
||||
if (backend == "files")
|
||||
return new ModMetadataDatabaseFiles(world_path);
|
||||
|
||||
|
@ -20,12 +20,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
// This file is an edited copy of test_authdatabase.cpp
|
||||
|
||||
#include "cmake_config.h"
|
||||
|
||||
#include "test.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include "database/database-dummy.h"
|
||||
#include "database/database-files.h"
|
||||
#include "database/database-sqlite3.h"
|
||||
#if USE_POSTGRESQL
|
||||
#include "database/database-postgresql.h"
|
||||
#endif
|
||||
#include "filesys.h"
|
||||
|
||||
namespace
|
||||
@ -104,6 +110,46 @@ private:
|
||||
std::string dir;
|
||||
ModMetadataDatabase *mod_meta_db = nullptr;
|
||||
};
|
||||
|
||||
#if USE_POSTGRESQL
|
||||
void clearPostgreSQLDatabase(const std::string &connect_string)
|
||||
{
|
||||
ModMetadataDatabasePostgreSQL db(connect_string);
|
||||
std::vector<std::string> modnames;
|
||||
db.beginSave();
|
||||
db.listMods(&modnames);
|
||||
for (const std::string &modname : modnames)
|
||||
db.removeModEntries(modname);
|
||||
db.endSave();
|
||||
}
|
||||
|
||||
class PostgreSQLProvider : public ModMetadataDatabaseProvider
|
||||
{
|
||||
public:
|
||||
PostgreSQLProvider(const std::string &connect_string): m_connect_string(connect_string) {}
|
||||
|
||||
~PostgreSQLProvider()
|
||||
{
|
||||
if (m_db)
|
||||
m_db->endSave();
|
||||
delete m_db;
|
||||
}
|
||||
|
||||
ModMetadataDatabase *getModMetadataDatabase() override
|
||||
{
|
||||
if (m_db)
|
||||
m_db->endSave();
|
||||
delete m_db;
|
||||
m_db = new ModMetadataDatabasePostgreSQL(m_connect_string);
|
||||
m_db->beginSave();
|
||||
return m_db;
|
||||
};
|
||||
|
||||
private:
|
||||
std::string m_connect_string;
|
||||
ModMetadataDatabase *m_db = nullptr;
|
||||
};
|
||||
#endif // USE_POSTGRESQL
|
||||
}
|
||||
|
||||
class TestModMetadataDatabase : public TestBase
|
||||
@ -193,6 +239,33 @@ void TestModMetadataDatabase::runTests(IGameDef *gamedef)
|
||||
runTestsForCurrentDB();
|
||||
|
||||
delete mod_meta_provider;
|
||||
|
||||
#if USE_POSTGRESQL
|
||||
const char *env_postgresql_connect_string = getenv("MINETEST_POSTGRESQL_CONNECT_STRING");
|
||||
if (env_postgresql_connect_string) {
|
||||
std::string connect_string(env_postgresql_connect_string);
|
||||
|
||||
rawstream << "-------- PostgreSQL database (same object)" << std::endl;
|
||||
|
||||
clearPostgreSQLDatabase(connect_string);
|
||||
mod_meta_db = new ModMetadataDatabasePostgreSQL(connect_string);
|
||||
mod_meta_provider = new FixedProvider(mod_meta_db);
|
||||
|
||||
runTestsForCurrentDB();
|
||||
|
||||
delete mod_meta_db;
|
||||
delete mod_meta_provider;
|
||||
|
||||
rawstream << "-------- PostgreSQL database (new objects)" << std::endl;
|
||||
|
||||
clearPostgreSQLDatabase(connect_string);
|
||||
mod_meta_provider = new PostgreSQLProvider(connect_string);
|
||||
|
||||
runTestsForCurrentDB();
|
||||
|
||||
delete mod_meta_provider;
|
||||
}
|
||||
#endif // USE_POSTGRESQL
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
Loading…
Reference in New Issue
Block a user