mirror of
https://github.com/minetest/minetestmapper.git
synced 2024-11-22 07:23:46 +01:00
PostgreSQL database support
This commit is contained in:
parent
d490cf0a31
commit
af502f3ac2
@ -78,6 +78,39 @@ if(NOT SQLITE3_LIBRARY OR NOT SQLITE3_INCLUDE_DIR)
|
|||||||
message(FATAL_ERROR "sqlite3 not found!")
|
message(FATAL_ERROR "sqlite3 not found!")
|
||||||
endif(NOT SQLITE3_LIBRARY OR NOT SQLITE3_INCLUDE_DIR)
|
endif(NOT SQLITE3_LIBRARY OR NOT SQLITE3_INCLUDE_DIR)
|
||||||
|
|
||||||
|
# Libraries: postgresql
|
||||||
|
|
||||||
|
option(ENABLE_POSTGRESQL "Enable PostgreSQL backend" TRUE)
|
||||||
|
set(USE_POSTGRESQL 0)
|
||||||
|
|
||||||
|
if(ENABLE_POSTGRESQL)
|
||||||
|
find_program(POSTGRESQL_CONFIG_EXECUTABLE pg_config DOC "pg_config")
|
||||||
|
find_library(POSTGRESQL_LIBRARY pq)
|
||||||
|
if(POSTGRESQL_CONFIG_EXECUTABLE)
|
||||||
|
execute_process(COMMAND ${POSTGRESQL_CONFIG_EXECUTABLE} --includedir-server
|
||||||
|
OUTPUT_VARIABLE POSTGRESQL_SERVER_INCLUDE_DIRS
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
execute_process(COMMAND ${POSTGRESQL_CONFIG_EXECUTABLE}
|
||||||
|
OUTPUT_VARIABLE POSTGRESQL_CLIENT_INCLUDE_DIRS
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
# This variable is case sensitive for the cmake PostgreSQL module
|
||||||
|
set(PostgreSQL_ADDITIONAL_SEARCH_PATHS ${POSTGRESQL_SERVER_INCLUDE_DIRS} ${POSTGRESQL_CLIENT_INCLUDE_DIRS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package("PostgreSQL")
|
||||||
|
|
||||||
|
if(POSTGRESQL_FOUND)
|
||||||
|
set(USE_POSTGRESQL 1)
|
||||||
|
message(STATUS "PostgreSQL backend enabled")
|
||||||
|
# This variable is case sensitive, don't try to change it to POSTGRESQL_INCLUDE_DIR
|
||||||
|
message(STATUS "PostgreSQL includes: ${PostgreSQL_INCLUDE_DIR}")
|
||||||
|
include_directories(${PostgreSQL_INCLUDE_DIR})
|
||||||
|
set(POSTGRESQL_LIBRARY ${PostgreSQL_LIBRARIES})
|
||||||
|
else()
|
||||||
|
message(STATUS "PostgreSQL not found!")
|
||||||
|
endif()
|
||||||
|
endif(ENABLE_POSTGRESQL)
|
||||||
|
|
||||||
# Libraries: leveldb
|
# Libraries: leveldb
|
||||||
|
|
||||||
set(USE_LEVELDB 0)
|
set(USE_LEVELDB 0)
|
||||||
@ -148,6 +181,10 @@ set(mapper_SRCS
|
|||||||
db-sqlite3.cpp
|
db-sqlite3.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(USE_POSTGRESQL)
|
||||||
|
set(mapper_SRCS ${mapper_SRCS} db-postgresql.cpp)
|
||||||
|
endif(USE_POSTGRESQL)
|
||||||
|
|
||||||
if(USE_LEVELDB)
|
if(USE_LEVELDB)
|
||||||
set(mapper_SRCS ${mapper_SRCS} db-leveldb.cpp)
|
set(mapper_SRCS ${mapper_SRCS} db-leveldb.cpp)
|
||||||
endif(USE_LEVELDB)
|
endif(USE_LEVELDB)
|
||||||
@ -163,6 +200,7 @@ add_executable(minetestmapper
|
|||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
minetestmapper
|
minetestmapper
|
||||||
${SQLITE3_LIBRARY}
|
${SQLITE3_LIBRARY}
|
||||||
|
${POSTGRESQL_LIBRARY}
|
||||||
${LEVELDB_LIBRARY}
|
${LEVELDB_LIBRARY}
|
||||||
${REDIS_LIBRARY}
|
${REDIS_LIBRARY}
|
||||||
${LIBGD_LIBRARY}
|
${LIBGD_LIBRARY}
|
||||||
|
@ -13,6 +13,9 @@
|
|||||||
#include "ZlibDecompressor.h"
|
#include "ZlibDecompressor.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "db-sqlite3.h"
|
#include "db-sqlite3.h"
|
||||||
|
#if USE_POSTGRESQL
|
||||||
|
#include "db-postgresql.h"
|
||||||
|
#endif
|
||||||
#if USE_LEVELDB
|
#if USE_LEVELDB
|
||||||
#include "db-leveldb.h"
|
#include "db-leveldb.h"
|
||||||
#endif
|
#endif
|
||||||
@ -283,6 +286,10 @@ void TileGenerator::openDb(const std::string &input)
|
|||||||
|
|
||||||
if(backend == "sqlite3")
|
if(backend == "sqlite3")
|
||||||
m_db = new DBSQLite3(input);
|
m_db = new DBSQLite3(input);
|
||||||
|
#if USE_POSTGRESQL
|
||||||
|
else if(backend == "postgresql")
|
||||||
|
m_db = new DBPostgreSQL(input);
|
||||||
|
#endif
|
||||||
#if USE_LEVELDB
|
#if USE_LEVELDB
|
||||||
else if(backend == "leveldb")
|
else if(backend == "leveldb")
|
||||||
m_db = new DBLevelDB(input);
|
m_db = new DBLevelDB(input);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#ifndef CMAKE_CONFIG_H
|
#ifndef CMAKE_CONFIG_H
|
||||||
#define CMAKE_CONFIG_H
|
#define CMAKE_CONFIG_H
|
||||||
|
|
||||||
|
#define USE_POSTGRESQL @USE_POSTGRESQL@
|
||||||
#define USE_LEVELDB @USE_LEVELDB@
|
#define USE_LEVELDB @USE_LEVELDB@
|
||||||
#define USE_REDIS @USE_REDIS@
|
#define USE_REDIS @USE_REDIS@
|
||||||
|
|
||||||
|
1
config.h
1
config.h
@ -9,6 +9,7 @@
|
|||||||
#ifdef USE_CMAKE_CONFIG_H
|
#ifdef USE_CMAKE_CONFIG_H
|
||||||
#include "cmake_config.h"
|
#include "cmake_config.h"
|
||||||
#else
|
#else
|
||||||
|
#define USE_POSTGRESQL 0
|
||||||
#define USE_LEVELDB 0
|
#define USE_LEVELDB 0
|
||||||
#define USE_REDIS 0
|
#define USE_REDIS 0
|
||||||
|
|
||||||
|
167
db-postgresql.cpp
Normal file
167
db-postgresql.cpp
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
#include <stdexcept>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include "db-postgresql.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#define ARRLEN(x) (sizeof(x) / sizeof((x)[0]))
|
||||||
|
|
||||||
|
DBPostgreSQL::DBPostgreSQL(const std::string &mapdir)
|
||||||
|
{
|
||||||
|
std::ifstream ifs((mapdir + "/world.mt").c_str());
|
||||||
|
if(!ifs.good())
|
||||||
|
throw std::runtime_error("Failed to read world.mt");
|
||||||
|
std::string const connect_string = get_setting("pgsql_connection", ifs);
|
||||||
|
ifs.close();
|
||||||
|
db = PQconnectdb(connect_string.c_str());
|
||||||
|
|
||||||
|
if (PQstatus(db) != CONNECTION_OK) {
|
||||||
|
throw std::runtime_error(std::string(
|
||||||
|
"PostgreSQL database error: ") +
|
||||||
|
PQerrorMessage(db)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareStatement(
|
||||||
|
"get_block_pos",
|
||||||
|
"SELECT posX, posY, posZ FROM blocks"
|
||||||
|
);
|
||||||
|
prepareStatement(
|
||||||
|
"get_blocks_z",
|
||||||
|
"SELECT posX, posY, data FROM blocks WHERE posZ = $1::int4"
|
||||||
|
);
|
||||||
|
|
||||||
|
checkResults(PQexec(db, "START TRANSACTION;"));
|
||||||
|
checkResults(PQexec(db, "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DBPostgreSQL::~DBPostgreSQL()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
checkResults(PQexec(db, "COMMIT;"));
|
||||||
|
} catch (std::exception& caught) {
|
||||||
|
std::cerr << "could not finalize: " << caught.what() << std::endl;
|
||||||
|
}
|
||||||
|
PQfinish(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<BlockPos> DBPostgreSQL::getBlockPos()
|
||||||
|
{
|
||||||
|
std::vector<BlockPos> positions;
|
||||||
|
|
||||||
|
PGresult *results = execPrepared(
|
||||||
|
"get_block_pos", 0,
|
||||||
|
NULL, NULL, NULL, false, false
|
||||||
|
);
|
||||||
|
|
||||||
|
int numrows = PQntuples(results);
|
||||||
|
|
||||||
|
for (int row = 0; row < numrows; ++row)
|
||||||
|
positions.push_back(pg_to_blockpos(results, row, 0));
|
||||||
|
|
||||||
|
PQclear(results);
|
||||||
|
|
||||||
|
return positions;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DBPostgreSQL::getBlocksOnZ(std::map<int16_t, BlockList> &blocks, int16_t zPos)
|
||||||
|
{
|
||||||
|
int32_t const z = htonl(zPos);
|
||||||
|
|
||||||
|
const void *args[] = { &z };
|
||||||
|
const int argLen[] = { sizeof(z) };
|
||||||
|
const int argFmt[] = { 1 };
|
||||||
|
|
||||||
|
PGresult *results = execPrepared(
|
||||||
|
"get_blocks_z", ARRLEN(args), args,
|
||||||
|
argLen, argFmt, false
|
||||||
|
);
|
||||||
|
|
||||||
|
int numrows = PQntuples(results);
|
||||||
|
|
||||||
|
for (int row = 0; row < numrows; ++row) {
|
||||||
|
BlockPos position;
|
||||||
|
position.x = pg_binary_to_int(results, row, 0);
|
||||||
|
position.y = pg_binary_to_int(results, row, 1);
|
||||||
|
position.z = zPos;
|
||||||
|
Block const b(
|
||||||
|
position,
|
||||||
|
ustring(
|
||||||
|
reinterpret_cast<unsigned char*>(
|
||||||
|
PQgetvalue(results, row, 2)
|
||||||
|
),
|
||||||
|
PQgetlength(results, row, 2)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
blocks[position.x].push_back(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
PQclear(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
PGresult *DBPostgreSQL::checkResults(PGresult *res, bool clear)
|
||||||
|
{
|
||||||
|
ExecStatusType statusType = PQresultStatus(res);
|
||||||
|
|
||||||
|
switch (statusType) {
|
||||||
|
case PGRES_COMMAND_OK:
|
||||||
|
case PGRES_TUPLES_OK:
|
||||||
|
break;
|
||||||
|
case PGRES_FATAL_ERROR:
|
||||||
|
throw std::runtime_error(
|
||||||
|
std::string("PostgreSQL database error: ") +
|
||||||
|
PQresultErrorMessage(res)
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Unhandled PostgreSQL result code"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clear)
|
||||||
|
PQclear(res);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DBPostgreSQL::prepareStatement(const std::string &name, const std::string &sql)
|
||||||
|
{
|
||||||
|
checkResults(PQprepare(db, name.c_str(), sql.c_str(), 0, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
PGresult *DBPostgreSQL::execPrepared(
|
||||||
|
const char *stmtName, const int paramsNumber,
|
||||||
|
const void **params,
|
||||||
|
const int *paramsLengths, const int *paramsFormats,
|
||||||
|
bool clear, bool nobinary
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return checkResults(PQexecPrepared(db, stmtName, paramsNumber,
|
||||||
|
(const char* const*) params, paramsLengths, paramsFormats,
|
||||||
|
nobinary ? 1 : 0), clear
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DBPostgreSQL::pg_to_int(PGresult *res, int row, int col)
|
||||||
|
{
|
||||||
|
return atoi(PQgetvalue(res, row, col));
|
||||||
|
}
|
||||||
|
|
||||||
|
int DBPostgreSQL::pg_binary_to_int(PGresult *res, int row, int col)
|
||||||
|
{
|
||||||
|
int32_t* raw = reinterpret_cast<int32_t*>(PQgetvalue(res, row, col));
|
||||||
|
return ntohl(*raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockPos DBPostgreSQL::pg_to_blockpos(PGresult *res, int row, int col)
|
||||||
|
{
|
||||||
|
BlockPos result;
|
||||||
|
result.x = pg_to_int(res, row, col);
|
||||||
|
result.y = pg_to_int(res, row, col + 1);
|
||||||
|
result.z = pg_to_int(res, row, col + 2);
|
||||||
|
return result;
|
||||||
|
}
|
29
db-postgresql.h
Normal file
29
db-postgresql.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#ifndef _DB_POSTGRESQL_H
|
||||||
|
#define _DB_POSTGRESQL_H
|
||||||
|
|
||||||
|
#include "db.h"
|
||||||
|
#include <libpq-fe.h>
|
||||||
|
|
||||||
|
class DBPostgreSQL : public DB {
|
||||||
|
public:
|
||||||
|
DBPostgreSQL(const std::string &mapdir);
|
||||||
|
virtual std::vector<BlockPos> getBlockPos();
|
||||||
|
virtual void getBlocksOnZ(std::map<int16_t, BlockList> &blocks, int16_t zPos);
|
||||||
|
virtual ~DBPostgreSQL();
|
||||||
|
protected:
|
||||||
|
PGresult *checkResults(PGresult *res, bool clear = true);
|
||||||
|
void prepareStatement(const std::string &name, const std::string &sql);
|
||||||
|
PGresult *execPrepared(
|
||||||
|
const char *stmtName, const int paramsNumber,
|
||||||
|
const void **params,
|
||||||
|
const int *paramsLengths = NULL, const int *paramsFormats = NULL,
|
||||||
|
bool clear = true, bool nobinary = true
|
||||||
|
);
|
||||||
|
int pg_to_int(PGresult *res, int row, int col);
|
||||||
|
int pg_binary_to_int(PGresult *res, int row, int col);
|
||||||
|
BlockPos pg_to_blockpos(PGresult *res, int row, int col);
|
||||||
|
private:
|
||||||
|
PGconn *db;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _DB_POSTGRESQL_H
|
Loading…
Reference in New Issue
Block a user