2 Copyright (C) 2016 Loic Blot <loic.blot@unix-experience.fr>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 2.1 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "database-postgresql.h"
26 // Without this some of the network functions are not found on mingw
28 #define _WIN32_WINNT 0x0501
33 #include <netinet/in.h>
37 #include "exceptions.h"
39 #include "remoteplayer.h"
40 #include "server/player_sao.h"
43 Database_PostgreSQL::Database_PostgreSQL(const std::string &connect_string,
45 m_connect_string(connect_string)
47 if (m_connect_string.empty()) {
48 // Use given type to reference the exact setting in the error message
51 "Set pgsql" + s + "_connection string in world.mt to "
52 "use the postgresql backend\n"
54 "pgsql" + s + "_connection has the following form: \n"
55 "\tpgsql" + s + "_connection = host=127.0.0.1 port=5432 "
56 "user=mt_user password=mt_password dbname=minetest" + s + "\n"
57 "mt_user should have CREATE TABLE, INSERT, SELECT, UPDATE and "
58 "DELETE rights on the database. "
59 "Don't create mt_user as a SUPERUSER!";
60 throw SettingNotFoundException(msg);
64 Database_PostgreSQL::~Database_PostgreSQL()
69 void Database_PostgreSQL::connectToDatabase()
71 m_conn = PQconnectdb(m_connect_string.c_str());
73 if (PQstatus(m_conn) != CONNECTION_OK) {
74 throw DatabaseException(std::string(
75 "PostgreSQL database error: ") +
76 PQerrorMessage(m_conn));
79 m_pgversion = PQserverVersion(m_conn);
82 * We are using UPSERT feature from PostgreSQL 9.5
83 * to have the better performance where possible.
85 if (m_pgversion < 90500) {
86 warningstream << "Your PostgreSQL server lacks UPSERT "
87 << "support. Use version 9.5 or better if possible."
91 infostream << "PostgreSQL Database: Version " << m_pgversion
92 << " Connection made." << std::endl;
98 void Database_PostgreSQL::verifyDatabase()
100 if (PQstatus(m_conn) == CONNECTION_OK)
107 void Database_PostgreSQL::ping()
109 if (PQping(m_connect_string.c_str()) != PQPING_OK) {
110 throw DatabaseException(std::string(
111 "PostgreSQL database error: ") +
112 PQerrorMessage(m_conn));
116 bool Database_PostgreSQL::initialized() const
118 return (PQstatus(m_conn) == CONNECTION_OK);
121 PGresult *Database_PostgreSQL::checkResults(PGresult *result, bool clear)
123 ExecStatusType statusType = PQresultStatus(result);
125 switch (statusType) {
126 case PGRES_COMMAND_OK:
127 case PGRES_TUPLES_OK:
129 case PGRES_FATAL_ERROR:
131 throw DatabaseException(
132 std::string("PostgreSQL database error: ") +
133 PQresultErrorMessage(result));
142 void Database_PostgreSQL::createTableIfNotExists(const std::string &table_name,
143 const std::string &definition)
145 std::string sql_check_table = "SELECT relname FROM pg_class WHERE relname='" +
147 PGresult *result = checkResults(PQexec(m_conn, sql_check_table.c_str()), false);
149 // If table doesn't exist, create it
150 if (!PQntuples(result)) {
151 checkResults(PQexec(m_conn, definition.c_str()));
157 void Database_PostgreSQL::beginSave()
160 checkResults(PQexec(m_conn, "BEGIN;"));
163 void Database_PostgreSQL::endSave()
165 checkResults(PQexec(m_conn, "COMMIT;"));
168 void Database_PostgreSQL::rollback()
170 checkResults(PQexec(m_conn, "ROLLBACK;"));
173 MapDatabasePostgreSQL::MapDatabasePostgreSQL(const std::string &connect_string):
174 Database_PostgreSQL(connect_string, ""),
181 void MapDatabasePostgreSQL::createDatabase()
183 createTableIfNotExists("blocks",
184 "CREATE TABLE blocks ("
189 "PRIMARY KEY (posX,posY,posZ)"
193 infostream << "PostgreSQL: Map Database was initialized." << std::endl;
196 void MapDatabasePostgreSQL::initStatements()
198 prepareStatement("read_block",
199 "SELECT data FROM blocks "
200 "WHERE posX = $1::int4 AND posY = $2::int4 AND "
203 if (getPGVersion() < 90500) {
204 prepareStatement("write_block_insert",
205 "INSERT INTO blocks (posX, posY, posZ, data) SELECT "
206 "$1::int4, $2::int4, $3::int4, $4::bytea "
207 "WHERE NOT EXISTS (SELECT true FROM blocks "
208 "WHERE posX = $1::int4 AND posY = $2::int4 AND "
211 prepareStatement("write_block_update",
212 "UPDATE blocks SET data = $4::bytea "
213 "WHERE posX = $1::int4 AND posY = $2::int4 AND "
216 prepareStatement("write_block",
217 "INSERT INTO blocks (posX, posY, posZ, data) VALUES "
218 "($1::int4, $2::int4, $3::int4, $4::bytea) "
219 "ON CONFLICT ON CONSTRAINT blocks_pkey DO "
220 "UPDATE SET data = $4::bytea");
223 prepareStatement("delete_block", "DELETE FROM blocks WHERE "
224 "posX = $1::int4 AND posY = $2::int4 AND posZ = $3::int4");
226 prepareStatement("list_all_loadable_blocks",
227 "SELECT posX, posY, posZ FROM blocks");
230 bool MapDatabasePostgreSQL::saveBlock(const v3s16 &pos, const std::string &data)
232 // Verify if we don't overflow the platform integer with the mapblock size
233 if (data.size() > INT_MAX) {
234 errorstream << "Database_PostgreSQL::saveBlock: Data truncation! "
235 << "data.size() over 0xFFFFFFFF (== " << data.size()
247 const void *args[] = { &x, &y, &z, data.c_str() };
248 const int argLen[] = {
249 sizeof(x), sizeof(y), sizeof(z), (int)data.size()
251 const int argFmt[] = { 1, 1, 1, 1 };
253 if (getPGVersion() < 90500) {
254 execPrepared("write_block_update", ARRLEN(args), args, argLen, argFmt);
255 execPrepared("write_block_insert", ARRLEN(args), args, argLen, argFmt);
257 execPrepared("write_block", ARRLEN(args), args, argLen, argFmt);
262 void MapDatabasePostgreSQL::loadBlock(const v3s16 &pos, std::string *block)
271 const void *args[] = { &x, &y, &z };
272 const int argLen[] = { sizeof(x), sizeof(y), sizeof(z) };
273 const int argFmt[] = { 1, 1, 1 };
275 PGresult *results = execPrepared("read_block", ARRLEN(args), args,
276 argLen, argFmt, false);
278 if (PQntuples(results))
279 *block = pg_to_string(results, 0, 0);
286 bool MapDatabasePostgreSQL::deleteBlock(const v3s16 &pos)
295 const void *args[] = { &x, &y, &z };
296 const int argLen[] = { sizeof(x), sizeof(y), sizeof(z) };
297 const int argFmt[] = { 1, 1, 1 };
299 execPrepared("delete_block", ARRLEN(args), args, argLen, argFmt);
304 void MapDatabasePostgreSQL::listAllLoadableBlocks(std::vector<v3s16> &dst)
308 PGresult *results = execPrepared("list_all_loadable_blocks", 0,
309 NULL, NULL, NULL, false, false);
311 int numrows = PQntuples(results);
313 for (int row = 0; row < numrows; ++row)
314 dst.push_back(pg_to_v3s16(results, row, 0));
322 PlayerDatabasePostgreSQL::PlayerDatabasePostgreSQL(const std::string &connect_string):
323 Database_PostgreSQL(connect_string, "_player"),
330 void PlayerDatabasePostgreSQL::createDatabase()
332 createTableIfNotExists("player",
333 "CREATE TABLE player ("
334 "name VARCHAR(60) NOT NULL,"
335 "pitch NUMERIC(15, 7) NOT NULL,"
336 "yaw NUMERIC(15, 7) NOT NULL,"
337 "posX NUMERIC(15, 7) NOT NULL,"
338 "posY NUMERIC(15, 7) NOT NULL,"
339 "posZ NUMERIC(15, 7) NOT NULL,"
341 "breath INT NOT NULL,"
342 "creation_date TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW(),"
343 "modification_date TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW(),"
348 createTableIfNotExists("player_inventories",
349 "CREATE TABLE player_inventories ("
350 "player VARCHAR(60) NOT NULL,"
351 "inv_id INT NOT NULL,"
352 "inv_width INT NOT NULL,"
353 "inv_name TEXT NOT NULL DEFAULT '',"
354 "inv_size INT NOT NULL,"
355 "PRIMARY KEY(player, inv_id),"
356 "CONSTRAINT player_inventories_fkey FOREIGN KEY (player) REFERENCES "
357 "player (name) ON DELETE CASCADE"
361 createTableIfNotExists("player_inventory_items",
362 "CREATE TABLE player_inventory_items ("
363 "player VARCHAR(60) NOT NULL,"
364 "inv_id INT NOT NULL,"
365 "slot_id INT NOT NULL,"
366 "item TEXT NOT NULL DEFAULT '',"
367 "PRIMARY KEY(player, inv_id, slot_id),"
368 "CONSTRAINT player_inventory_items_fkey FOREIGN KEY (player) REFERENCES "
369 "player (name) ON DELETE CASCADE"
373 createTableIfNotExists("player_metadata",
374 "CREATE TABLE player_metadata ("
375 "player VARCHAR(60) NOT NULL,"
376 "attr VARCHAR(256) NOT NULL,"
378 "PRIMARY KEY(player, attr),"
379 "CONSTRAINT player_metadata_fkey FOREIGN KEY (player) REFERENCES "
380 "player (name) ON DELETE CASCADE"
384 infostream << "PostgreSQL: Player Database was inited." << std::endl;
387 void PlayerDatabasePostgreSQL::initStatements()
389 if (getPGVersion() < 90500) {
390 prepareStatement("create_player",
391 "INSERT INTO player(name, pitch, yaw, posX, posY, posZ, hp, breath) VALUES "
392 "($1, $2, $3, $4, $5, $6, $7::int, $8::int)");
394 prepareStatement("update_player",
395 "UPDATE SET pitch = $2, yaw = $3, posX = $4, posY = $5, posZ = $6, hp = $7::int, "
396 "breath = $8::int, modification_date = NOW() WHERE name = $1");
398 prepareStatement("save_player",
399 "INSERT INTO player(name, pitch, yaw, posX, posY, posZ, hp, breath) VALUES "
400 "($1, $2, $3, $4, $5, $6, $7::int, $8::int)"
401 "ON CONFLICT ON CONSTRAINT player_pkey DO UPDATE SET pitch = $2, yaw = $3, "
402 "posX = $4, posY = $5, posZ = $6, hp = $7::int, breath = $8::int, "
403 "modification_date = NOW()");
406 prepareStatement("remove_player", "DELETE FROM player WHERE name = $1");
408 prepareStatement("load_player_list", "SELECT name FROM player");
410 prepareStatement("remove_player_inventories",
411 "DELETE FROM player_inventories WHERE player = $1");
413 prepareStatement("remove_player_inventory_items",
414 "DELETE FROM player_inventory_items WHERE player = $1");
416 prepareStatement("add_player_inventory",
417 "INSERT INTO player_inventories (player, inv_id, inv_width, inv_name, inv_size) VALUES "
418 "($1, $2::int, $3::int, $4, $5::int)");
420 prepareStatement("add_player_inventory_item",
421 "INSERT INTO player_inventory_items (player, inv_id, slot_id, item) VALUES "
422 "($1, $2::int, $3::int, $4)");
424 prepareStatement("load_player_inventories",
425 "SELECT inv_id, inv_width, inv_name, inv_size FROM player_inventories "
426 "WHERE player = $1 ORDER BY inv_id");
428 prepareStatement("load_player_inventory_items",
429 "SELECT slot_id, item FROM player_inventory_items WHERE "
430 "player = $1 AND inv_id = $2::int");
432 prepareStatement("load_player",
433 "SELECT pitch, yaw, posX, posY, posZ, hp, breath FROM player WHERE name = $1");
435 prepareStatement("remove_player_metadata",
436 "DELETE FROM player_metadata WHERE player = $1");
438 prepareStatement("save_player_metadata",
439 "INSERT INTO player_metadata (player, attr, value) VALUES ($1, $2, $3)");
441 prepareStatement("load_player_metadata",
442 "SELECT attr, value FROM player_metadata WHERE player = $1");
446 bool PlayerDatabasePostgreSQL::playerDataExists(const std::string &playername)
450 const char *values[] = { playername.c_str() };
451 PGresult *results = execPrepared("load_player", 1, values, false);
453 bool res = (PQntuples(results) > 0);
458 void PlayerDatabasePostgreSQL::savePlayer(RemotePlayer *player)
460 PlayerSAO* sao = player->getPlayerSAO();
466 v3f pos = sao->getBasePosition();
467 std::string pitch = ftos(sao->getLookPitch());
468 std::string yaw = ftos(sao->getRotation().Y);
469 std::string posx = ftos(pos.X);
470 std::string posy = ftos(pos.Y);
471 std::string posz = ftos(pos.Z);
472 std::string hp = itos(sao->getHP());
473 std::string breath = itos(sao->getBreath());
474 const char *values[] = {
478 posx.c_str(), posy.c_str(), posz.c_str(),
483 const char* rmvalues[] = { player->getName() };
486 if (getPGVersion() < 90500) {
487 if (!playerDataExists(player->getName()))
488 execPrepared("create_player", 8, values, true, false);
490 execPrepared("update_player", 8, values, true, false);
493 execPrepared("save_player", 8, values, true, false);
495 // Write player inventories
496 execPrepared("remove_player_inventories", 1, rmvalues);
497 execPrepared("remove_player_inventory_items", 1, rmvalues);
499 const auto &inventory_lists = sao->getInventory()->getLists();
500 std::ostringstream oss;
501 for (u16 i = 0; i < inventory_lists.size(); i++) {
502 const InventoryList* list = inventory_lists[i];
503 const std::string &name = list->getName();
504 std::string width = itos(list->getWidth()),
505 inv_id = itos(i), lsize = itos(list->getSize());
507 const char* inv_values[] = {
514 execPrepared("add_player_inventory", 5, inv_values);
516 for (u32 j = 0; j < list->getSize(); j++) {
519 list->getItem(j).serialize(oss);
520 std::string itemStr = oss.str(), slotId = itos(j);
522 const char* invitem_values[] = {
528 execPrepared("add_player_inventory_item", 4, invitem_values);
532 execPrepared("remove_player_metadata", 1, rmvalues);
533 const StringMap &attrs = sao->getMeta().getStrings();
534 for (const auto &attr : attrs) {
535 const char *meta_values[] = {
540 execPrepared("save_player_metadata", 3, meta_values);
544 player->onSuccessfulSave();
547 bool PlayerDatabasePostgreSQL::loadPlayer(RemotePlayer *player, PlayerSAO *sao)
552 const char *values[] = { player->getName() };
553 PGresult *results = execPrepared("load_player", 1, values, false, false);
555 // Player not found, return not found
556 if (!PQntuples(results)) {
561 sao->setLookPitch(pg_to_float(results, 0, 0));
562 sao->setRotation(v3f(0, pg_to_float(results, 0, 1), 0));
563 sao->setBasePosition(v3f(
564 pg_to_float(results, 0, 2),
565 pg_to_float(results, 0, 3),
566 pg_to_float(results, 0, 4))
568 sao->setHPRaw((u16) pg_to_int(results, 0, 5));
569 sao->setBreath((u16) pg_to_int(results, 0, 6), false);
574 results = execPrepared("load_player_inventories", 1, values, false, false);
576 int resultCount = PQntuples(results);
578 for (int row = 0; row < resultCount; ++row) {
579 InventoryList* invList = player->inventory.
580 addList(PQgetvalue(results, row, 2), pg_to_uint(results, row, 3));
581 invList->setWidth(pg_to_uint(results, row, 1));
583 u32 invId = pg_to_uint(results, row, 0);
584 std::string invIdStr = itos(invId);
586 const char* values2[] = {
590 PGresult *results2 = execPrepared("load_player_inventory_items", 2,
591 values2, false, false);
593 int resultCount2 = PQntuples(results2);
594 for (int row2 = 0; row2 < resultCount2; row2++) {
595 const std::string itemStr = PQgetvalue(results2, row2, 1);
596 if (itemStr.length() > 0) {
598 stack.deSerialize(itemStr);
599 invList->changeItem(pg_to_uint(results2, row2, 0), stack);
607 results = execPrepared("load_player_metadata", 1, values, false);
609 int numrows = PQntuples(results);
610 for (int row = 0; row < numrows; row++) {
611 sao->getMeta().setString(PQgetvalue(results, row, 0), PQgetvalue(results, row, 1));
613 sao->getMeta().setModified(false);
620 bool PlayerDatabasePostgreSQL::removePlayer(const std::string &name)
622 if (!playerDataExists(name))
627 const char *values[] = { name.c_str() };
628 execPrepared("remove_player", 1, values);
633 void PlayerDatabasePostgreSQL::listPlayers(std::vector<std::string> &res)
637 PGresult *results = execPrepared("load_player_list", 0, NULL, false);
639 int numrows = PQntuples(results);
640 for (int row = 0; row < numrows; row++)
641 res.emplace_back(PQgetvalue(results, row, 0));
646 AuthDatabasePostgreSQL::AuthDatabasePostgreSQL(const std::string &connect_string) :
647 Database_PostgreSQL(connect_string, "_auth"),
653 void AuthDatabasePostgreSQL::createDatabase()
655 createTableIfNotExists("auth",
656 "CREATE TABLE auth ("
660 "last_login INT NOT NULL DEFAULT 0,"
664 createTableIfNotExists("user_privileges",
665 "CREATE TABLE user_privileges ("
668 "PRIMARY KEY (id, privilege),"
669 "CONSTRAINT fk_id FOREIGN KEY (id) REFERENCES auth (id) ON DELETE CASCADE"
673 void AuthDatabasePostgreSQL::initStatements()
675 prepareStatement("auth_read", "SELECT id, name, password, last_login FROM auth WHERE name = $1");
676 prepareStatement("auth_write", "UPDATE auth SET name = $1, password = $2, last_login = $3 WHERE id = $4");
677 prepareStatement("auth_create", "INSERT INTO auth (name, password, last_login) VALUES ($1, $2, $3) RETURNING id");
678 prepareStatement("auth_delete", "DELETE FROM auth WHERE name = $1");
680 prepareStatement("auth_list_names", "SELECT name FROM auth ORDER BY name DESC");
682 prepareStatement("auth_read_privs", "SELECT privilege FROM user_privileges WHERE id = $1");
683 prepareStatement("auth_write_privs", "INSERT INTO user_privileges (id, privilege) VALUES ($1, $2)");
684 prepareStatement("auth_delete_privs", "DELETE FROM user_privileges WHERE id = $1");
687 bool AuthDatabasePostgreSQL::getAuth(const std::string &name, AuthEntry &res)
691 const char *values[] = { name.c_str() };
692 PGresult *result = execPrepared("auth_read", 1, values, false, false);
693 int numrows = PQntuples(result);
699 res.id = pg_to_uint(result, 0, 0);
700 res.name = pg_to_string(result, 0, 1);
701 res.password = pg_to_string(result, 0, 2);
702 res.last_login = pg_to_int(result, 0, 3);
706 std::string playerIdStr = itos(res.id);
707 const char *privsValues[] = { playerIdStr.c_str() };
708 PGresult *results = execPrepared("auth_read_privs", 1, privsValues, false);
710 numrows = PQntuples(results);
711 for (int row = 0; row < numrows; row++)
712 res.privileges.emplace_back(PQgetvalue(results, row, 0));
719 bool AuthDatabasePostgreSQL::saveAuth(const AuthEntry &authEntry)
725 std::string lastLoginStr = itos(authEntry.last_login);
726 std::string idStr = itos(authEntry.id);
727 const char *values[] = {
728 authEntry.name.c_str() ,
729 authEntry.password.c_str(),
730 lastLoginStr.c_str(),
733 execPrepared("auth_write", 4, values);
735 writePrivileges(authEntry);
741 bool AuthDatabasePostgreSQL::createAuth(AuthEntry &authEntry)
745 std::string lastLoginStr = itos(authEntry.last_login);
746 const char *values[] = {
747 authEntry.name.c_str() ,
748 authEntry.password.c_str(),
754 PGresult *result = execPrepared("auth_create", 3, values, false, false);
756 int numrows = PQntuples(result);
758 errorstream << "Strange behavior on auth creation, no ID returned." << std::endl;
764 authEntry.id = pg_to_uint(result, 0, 0);
767 writePrivileges(authEntry);
773 bool AuthDatabasePostgreSQL::deleteAuth(const std::string &name)
777 const char *values[] = { name.c_str() };
778 execPrepared("auth_delete", 1, values);
780 // privileges deleted by foreign key on delete cascade
784 void AuthDatabasePostgreSQL::listNames(std::vector<std::string> &res)
788 PGresult *results = execPrepared("auth_list_names", 0,
789 NULL, NULL, NULL, false, false);
791 int numrows = PQntuples(results);
793 for (int row = 0; row < numrows; ++row)
794 res.emplace_back(PQgetvalue(results, row, 0));
799 void AuthDatabasePostgreSQL::reload()
804 void AuthDatabasePostgreSQL::writePrivileges(const AuthEntry &authEntry)
806 std::string authIdStr = itos(authEntry.id);
807 const char *values[] = { authIdStr.c_str() };
808 execPrepared("auth_delete_privs", 1, values);
810 for (const std::string &privilege : authEntry.privileges) {
811 const char *values[] = { authIdStr.c_str(), privilege.c_str() };
812 execPrepared("auth_write_privs", 2, values);
816 ModStorageDatabasePostgreSQL::ModStorageDatabasePostgreSQL(const std::string &connect_string):
817 Database_PostgreSQL(connect_string, "_mod_storage"),
823 void ModStorageDatabasePostgreSQL::createDatabase()
825 createTableIfNotExists("mod_storage",
826 "CREATE TABLE mod_storage ("
827 "modname TEXT NOT NULL,"
828 "key BYTEA NOT NULL,"
829 "value BYTEA NOT NULL,"
830 "PRIMARY KEY (modname, key)"
833 infostream << "PostgreSQL: Mod Storage Database was initialized." << std::endl;
836 void ModStorageDatabasePostgreSQL::initStatements()
838 prepareStatement("get_all",
839 "SELECT key, value FROM mod_storage WHERE modname = $1");
840 prepareStatement("get_all_keys",
841 "SELECT key FROM mod_storage WHERE modname = $1");
842 prepareStatement("get",
843 "SELECT value FROM mod_storage WHERE modname = $1 AND key = $2::bytea");
844 prepareStatement("has",
845 "SELECT true FROM mod_storage WHERE modname = $1 AND key = $2::bytea");
846 if (getPGVersion() < 90500) {
847 prepareStatement("set_insert",
848 "INSERT INTO mod_storage (modname, key, value) "
849 "SELECT $1, $2::bytea, $3::bytea "
851 "SELECT true FROM mod_storage WHERE modname = $1 AND key = $2::bytea"
853 prepareStatement("set_update",
854 "UPDATE mod_storage SET value = $3::bytea WHERE modname = $1 AND key = $2::bytea");
856 prepareStatement("set",
857 "INSERT INTO mod_storage (modname, key, value) VALUES ($1, $2::bytea, $3::bytea) "
858 "ON CONFLICT ON CONSTRAINT mod_storage_pkey DO "
859 "UPDATE SET value = $3::bytea");
861 prepareStatement("remove",
862 "DELETE FROM mod_storage WHERE modname = $1 AND key = $2::bytea");
863 prepareStatement("remove_all",
864 "DELETE FROM mod_storage WHERE modname = $1");
865 prepareStatement("list",
866 "SELECT DISTINCT modname FROM mod_storage");
869 void ModStorageDatabasePostgreSQL::getModEntries(const std::string &modname, StringMap *storage)
873 const void *args[] = { modname.c_str() };
874 const int argLen[] = { -1 };
875 const int argFmt[] = { 0 };
876 PGresult *results = execPrepared("get_all", ARRLEN(args),
877 args, argLen, argFmt, false);
879 int numrows = PQntuples(results);
881 for (int row = 0; row < numrows; ++row)
882 (*storage)[pg_to_string(results, row, 0)] = pg_to_string(results, row, 1);
887 void ModStorageDatabasePostgreSQL::getModKeys(const std::string &modname,
888 std::vector<std::string> *storage)
892 const void *args[] = { modname.c_str() };
893 const int argLen[] = { -1 };
894 const int argFmt[] = { 0 };
895 PGresult *results = execPrepared("get_all_keys", ARRLEN(args),
896 args, argLen, argFmt, false);
898 int numrows = PQntuples(results);
900 storage->reserve(storage->size() + numrows);
901 for (int row = 0; row < numrows; ++row)
902 storage->push_back(pg_to_string(results, row, 0));
907 bool ModStorageDatabasePostgreSQL::getModEntry(const std::string &modname,
908 const std::string &key, std::string *value)
912 const void *args[] = { modname.c_str(), key.c_str() };
913 const int argLen[] = { -1, (int)MYMIN(key.size(), INT_MAX) };
914 const int argFmt[] = { 0, 1 };
915 PGresult *results = execPrepared("get", ARRLEN(args), args, argLen, argFmt, false);
917 int numrows = PQntuples(results);
918 bool found = numrows > 0;
921 *value = pg_to_string(results, 0, 0);
928 bool ModStorageDatabasePostgreSQL::hasModEntry(const std::string &modname,
929 const std::string &key)
933 const void *args[] = { modname.c_str(), key.c_str() };
934 const int argLen[] = { -1, (int)MYMIN(key.size(), INT_MAX) };
935 const int argFmt[] = { 0, 1 };
936 PGresult *results = execPrepared("has", ARRLEN(args), args, argLen, argFmt, false);
938 int numrows = PQntuples(results);
939 bool found = numrows > 0;
946 bool ModStorageDatabasePostgreSQL::setModEntry(const std::string &modname,
947 const std::string &key, const std::string &value)
951 const void *args[] = { modname.c_str(), key.c_str(), value.c_str() };
952 const int argLen[] = {
954 (int)MYMIN(key.size(), INT_MAX),
955 (int)MYMIN(value.size(), INT_MAX),
957 const int argFmt[] = { 0, 1, 1 };
958 if (getPGVersion() < 90500) {
959 execPrepared("set_insert", ARRLEN(args), args, argLen, argFmt);
960 execPrepared("set_update", ARRLEN(args), args, argLen, argFmt);
962 execPrepared("set", ARRLEN(args), args, argLen, argFmt);
968 bool ModStorageDatabasePostgreSQL::removeModEntry(const std::string &modname,
969 const std::string &key)
973 const void *args[] = { modname.c_str(), key.c_str() };
974 const int argLen[] = { -1, (int)MYMIN(key.size(), INT_MAX) };
975 const int argFmt[] = { 0, 1 };
976 PGresult *results = execPrepared("remove", ARRLEN(args), args, argLen, argFmt, false);
978 int affected = atoi(PQcmdTuples(results));
985 bool ModStorageDatabasePostgreSQL::removeModEntries(const std::string &modname)
989 const void *args[] = { modname.c_str() };
990 const int argLen[] = { -1 };
991 const int argFmt[] = { 0 };
992 PGresult *results = execPrepared("remove_all", ARRLEN(args), args, argLen, argFmt, false);
994 int affected = atoi(PQcmdTuples(results));
1001 void ModStorageDatabasePostgreSQL::listMods(std::vector<std::string> *res)
1005 PGresult *results = execPrepared("list", 0, NULL, false);
1007 int numrows = PQntuples(results);
1009 for (int row = 0; row < numrows; ++row)
1010 res->push_back(pg_to_string(results, row, 0));
1016 #endif // USE_POSTGRESQL