Add postgresql support

Supports both ShadowNinja's implementation:
  https://github.com/ShadowNinja/minetest/tree/PostgreSQL
and johnnyjoy's implementation:
  https://forum.minetest.net/viewtopic.php?f=3&t=12851
  4d537fe53a
This commit is contained in:
Rogier 2015-12-16 15:15:47 +01:00
parent 4e8441d322
commit 919595c614
12 changed files with 242 additions and 19 deletions

View File

@ -144,12 +144,14 @@ include(FindPackageHandleStandardArgs)
# Find database(s) # Find database(s)
set(USE_SQLITE3 0) set(USE_SQLITE3 0)
set(USE_POSTGRESQL 0)
set(USE_LEVELDB 0) set(USE_LEVELDB 0)
set(USE_REDIS 0) set(USE_REDIS 0)
OPTION(ENABLE_ANY_DATABASE "Enable any available database backends" True) OPTION(ENABLE_ANY_DATABASE "Enable any available database backends" True)
OPTION(ENABLE_ALL_DATABASES "Enable all possible database backends") OPTION(ENABLE_ALL_DATABASES "Enable all possible database backends")
OPTION(ENABLE_SQLITE3 "Enable sqlite3 backend" True) OPTION(ENABLE_SQLITE3 "Enable sqlite3 backend" True)
OPTION(ENABLE_POSTGRESQL "Enable postgresql backend")
OPTION(ENABLE_LEVELDB "Enable LevelDB backend") OPTION(ENABLE_LEVELDB "Enable LevelDB backend")
OPTION(ENABLE_REDIS "Enable redis backend") OPTION(ENABLE_REDIS "Enable redis backend")
@ -173,6 +175,26 @@ if(ENABLE_SQLITE3 OR ENABLE_ANY_DATABASE OR ENABLE_ALL_DATABASES)
endif(SQLITE3_LIBRARY AND SQLITE3_INCLUDE_DIR) endif(SQLITE3_LIBRARY AND SQLITE3_INCLUDE_DIR)
endif(ENABLE_SQLITE3 OR ENABLE_ANY_DATABASE OR ENABLE_ALL_DATABASES) endif(ENABLE_SQLITE3 OR ENABLE_ANY_DATABASE OR ENABLE_ALL_DATABASES)
# Find postgresql
if(ENABLE_POSTGRESQL OR ENABLE_ANY_DATABASE OR ENABLE_ALL_DATABASES)
find_library(POSTGRESQL_LIBRARY pq)
find_path(POSTGRESQL_INCLUDE_DIR libpq-fe.h PATH_SUFFIXES postgresql)
message (STATUS "postgresql library: ${POSTGRESQL_LIBRARY}")
message (STATUS "postgresql headers: ${POSTGRESQL_INCLUDE_DIR}")
if(POSTGRESQL_LIBRARY AND POSTGRESQL_INCLUDE_DIR)
set(USE_POSTGRESQL 1)
message(STATUS "postgresql backend enabled")
include_directories(${POSTGRESQL_INCLUDE_DIR})
else(POSTGRESQL_LIBRARY AND POSTGRESQL_INCLUDE_DIR)
set(USE_POSTGRESQL 0)
if(ENABLE_POSTGRESQL OR ENABLE_ALL_DATABASES)
message(SEND_ERROR "postgresql backend requested but postgresql libraries not found!")
else(ENABLE_POSTGRESQL OR ENABLE_ALL_DATABASES)
message(STATUS "postgresql not enabled (postgresql libraries and/or headers not found)")
endif(ENABLE_POSTGRESQL OR ENABLE_ALL_DATABASES)
endif(POSTGRESQL_LIBRARY AND POSTGRESQL_INCLUDE_DIR)
endif(ENABLE_POSTGRESQL OR ENABLE_ANY_DATABASE OR ENABLE_ALL_DATABASES)
# Find leveldb # Find leveldb
if(ENABLE_LEVELDB OR ENABLE_ANY_DATABASE OR ENABLE_ALL_DATABASES) if(ENABLE_LEVELDB OR ENABLE_ANY_DATABASE OR ENABLE_ALL_DATABASES)
find_library(LEVELDB_LIBRARY leveldb) find_library(LEVELDB_LIBRARY leveldb)
@ -213,9 +235,9 @@ if(ENABLE_REDIS OR ENABLE_ANY_DATABASE OR ENABLE_ALL_DATABASES)
endif(REDIS_LIBRARY AND REDIS_INCLUDE_DIR) endif(REDIS_LIBRARY AND REDIS_INCLUDE_DIR)
endif(ENABLE_REDIS OR ENABLE_ANY_DATABASE OR ENABLE_ALL_DATABASES) endif(ENABLE_REDIS OR ENABLE_ANY_DATABASE OR ENABLE_ALL_DATABASES)
if(NOT USE_SQLITE3 AND NOT USE_LEVELDB AND NOT USE_REDIS) if(NOT USE_SQLITE3 AND NOT USE_POSTGRESQL AND NOT USE_LEVELDB AND NOT USE_REDIS)
message(SEND_ERROR "No database backends are configured, or none could be found") message(SEND_ERROR "No database backends are configured, or none could be found")
endif(NOT USE_SQLITE3 AND NOT USE_LEVELDB AND NOT USE_REDIS) endif(NOT USE_SQLITE3 AND NOT USE_POSTGRESQL AND NOT USE_LEVELDB AND NOT USE_REDIS)
include_directories( include_directories(
"${PROJECT_BINARY_DIR}" "${PROJECT_BINARY_DIR}"
@ -247,6 +269,11 @@ if(USE_SQLITE3)
set(LINK_LIBRARIES ${LINK_LIBRARIES} ${SQLITE3_LIBRARY}) set(LINK_LIBRARIES ${LINK_LIBRARIES} ${SQLITE3_LIBRARY})
endif(USE_SQLITE3) endif(USE_SQLITE3)
if(USE_POSTGRESQL)
set(mapper_SRCS ${mapper_SRCS} db-postgresql.cpp)
set(LINK_LIBRARIES ${LINK_LIBRARIES} ${POSTGRESQL_LIBRARY})
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)
set(LINK_LIBRARIES ${LINK_LIBRARIES} ${LEVELDB_LIBRARY}) set(LINK_LIBRARIES ${LINK_LIBRARIES} ${LEVELDB_LIBRARY})

View File

@ -1,4 +1,7 @@
[] []
Features:
- Support for postgresql backend added
Compatible with both ShadowNinja's and johnnyjoy's implementation
Enhancements: Enhancements:
- Updated the included colors file with colors for the new tiles that - Updated the included colors file with colors for the new tiles that
were recently added to the default minetest game. were recently added to the default minetest game.

View File

@ -22,7 +22,7 @@ Map Generation Features
----------------------- -----------------------
* Support for both minetest and freeminer worlds * Support for both minetest and freeminer worlds
* Support for sqlite3, leveldb and redis map databases * Support for sqlite3, postgresql, leveldb and redis map databases
* Generate a subsection of the map, or a full map * Generate a subsection of the map, or a full map
(but note that the size of generated images is limited) (but note that the size of generated images is limited)
* Generate regular maps or height-maps * Generate regular maps or height-maps
@ -62,6 +62,7 @@ For more detailed instructions, see `<doc/build-instructions.rst>`_.
* zlib * zlib
* libgd * libgd
* sqlite3 (not mandatory. See `<doc/build-instructions.rst>`_. * sqlite3 (not mandatory. See `<doc/build-instructions.rst>`_.
* postgresql (if postgresql support is desired)
* leveldb (if leveldb support is desired) * leveldb (if leveldb support is desired)
* hiredis (if redis support is desired) * hiredis (if redis support is desired)

View File

@ -27,6 +27,9 @@
#if USE_SQLITE3 #if USE_SQLITE3
#include "db-sqlite3.h" #include "db-sqlite3.h"
#endif #endif
#if USE_POSTGRESQL
#include "db-postgresql.h"
#endif
#if USE_LEVELDB #if USE_LEVELDB
#include "db-leveldb.h" #include "db-leveldb.h"
#endif #endif
@ -775,6 +778,14 @@ void TileGenerator::openDb(const std::string &input)
m_db = db = new DBSQLite3(input); m_db = db = new DBSQLite3(input);
#else #else
unsupported = true; unsupported = true;
#endif
}
else if (backend == "postgresql") {
#if USE_POSTGRESQL
DBPostgreSQL *db;
m_db = db = new DBPostgreSQL(input);
#else
unsupported = true;
#endif #endif
} }
else if (backend == "leveldb") { else if (backend == "leveldb") {

View File

@ -4,6 +4,7 @@
#define CMAKE_CONFIG_H #define CMAKE_CONFIG_H
#define USE_SQLITE3 @USE_SQLITE3@ #define USE_SQLITE3 @USE_SQLITE3@
#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@

View File

@ -36,6 +36,12 @@
#define USAGE_NAME_SQLITE #define USAGE_NAME_SQLITE
#endif #endif
#if USE_POSTGRESQL
#define USAGE_NAME_POSTGRESQL "/postgresql"
#else
#define USAGE_NAME_POSTGRESQL
#endif
#if USE_LEVELDB #if USE_LEVELDB
#define USAGE_NAME_LEVELDB "/leveldb" #define USAGE_NAME_LEVELDB "/leveldb"
#else #else
@ -48,18 +54,20 @@
#define USAGE_NAME_REDIS #define USAGE_NAME_REDIS
#endif #endif
#define USAGE_DATABASES "auto" USAGE_NAME_SQLITE USAGE_NAME_LEVELDB USAGE_NAME_REDIS #define USAGE_DATABASES "auto" USAGE_NAME_SQLITE USAGE_NAME_POSTGRESQL USAGE_NAME_LEVELDB USAGE_NAME_REDIS
#if !USE_SQLITE3 && !USE_LEVELDB && !USE_REDIS #if !USE_SQLITE3 && !USE_LEVELDB && !USE_REDIS
#error No database backends configured ! #error No database backends configured !
#endif #endif
// default database to use // default database to use
#if USE_SQLITE3 && !USE_LEVELDB && !USE_REDIS #if USE_SQLITE3 && !USE_POSTGRESQL && !USE_LEVELDB && !USE_REDIS
#define DEFAULT_BACKEND "sqlite3" #define DEFAULT_BACKEND "sqlite3"
#elif !USE_SQLITE3 && USE_LEVELDB && !USE_REDIS #elif !USE_SQLITE3 && USE_POSTGRESQL && !USE_LEVELDB && !USE_REDIS
#define DEFAULT_BACKEND "postgresql"
#elif !USE_SQLITE3 && !USE_POSTGRESQL && USE_LEVELDB && !USE_REDIS
#define DEFAULT_BACKEND "leveldb" #define DEFAULT_BACKEND "leveldb"
#elif !USE_SQLITE3 && !USE_LEVELDB && USE_REDIS #elif !USE_SQLITE3 && !USE_POSTGRESQL && !USE_LEVELDB && USE_REDIS
#define DEFAULT_BACKEND "redis" #define DEFAULT_BACKEND "redis"
#else #else
#define DEFAULT_BACKEND "auto" #define DEFAULT_BACKEND "auto"

127
db-postgresql.cpp Normal file
View File

@ -0,0 +1,127 @@
#include "db-postgresql.h"
#include <stdexcept>
#include <unistd.h> // for usleep
#include <arpa/inet.h>
#include "Settings.h"
#include "types.h"
#define BLOCKPOSLIST_QUERY "SELECT x, y, z FROM blocks"
#define BLOCK_QUERY "SELECT data FROM blocks WHERE x = $1 AND y = $2 AND z = $3"
// From pg_type.h
#define PG_INT4OID 23
DBPostgreSQL::DBPostgreSQL(const std::string &mapdir) :
m_blocksQueriedCount(0),
m_blocksReadCount(0)
{
Settings world_mt(mapdir + "/world.mt");
std::string connection_info;
bool info_found = false;
// ShadowNinja's implementation
info_found = world_mt.check("postgresql_connection_info", connection_info);
// johnnyjoy's implementation
// The default value is not used here as it seems to me it has a serious issue:
// creating two worlds without specifying pg_connection_info will result in both
// worlds using the same database.
if (!info_found)
info_found = world_mt.check("pg_connection_info", connection_info);
if (!info_found)
throw std::runtime_error("Set postgresql_connection_info or pg_connection_info in world.mt to use the postgresql backend");
m_connection = PQconnectdb(connection_info.c_str());
if (PQstatus(m_connection) != CONNECTION_OK) {
throw std::runtime_error(std::string("Failed to connect to postgresql database: ")
+ PQerrorMessage(m_connection));
}
PGresult *result;
result = PQprepare(m_connection, "GetBlockPosList", BLOCKPOSLIST_QUERY, 0, NULL);
if (!result || PQresultStatus(result) != PGRES_COMMAND_OK)
throw std::runtime_error(std::string("Failed to prepare PostgreSQL statement (GetBlockPosList): ")
+ (result ? PQresultErrorMessage(result) : "(result was NULL)"));
PQclear(result);
result = PQprepare(m_connection, "GetBlock", BLOCK_QUERY, 0, NULL);
if (!result || PQresultStatus(result) != PGRES_COMMAND_OK)
throw std::runtime_error(std::string("Failed to prepare PostgreSQL statement (GetBlock): ")
+ (result ? PQresultErrorMessage(result) : "(result was NULL)"));
PQclear(result);
for (int i = 0; i < 3; i++) {
m_getBlockParamList[i] = reinterpret_cast<char const *>(m_getBlockParams + i);
m_getBlockParamLengths[i] = sizeof(int32_t);
m_getBlockParamFormats[i] = 1;
}
}
DBPostgreSQL::~DBPostgreSQL()
{
PQfinish(m_connection);
}
int DBPostgreSQL::getBlocksReadCount(void)
{
return m_blocksReadCount;
}
int DBPostgreSQL::getBlocksQueriedCount(void)
{
return m_blocksQueriedCount;
}
const DB::BlockPosList &DBPostgreSQL::getBlockPos() {
m_blockPosList.clear();
PGresult *result = PQexecPrepared(m_connection, "GetBlockPosList", 0, NULL, NULL, NULL, 1);
if (!result || PQresultStatus(result) != PGRES_TUPLES_OK)
throw std::runtime_error(std::string("Failed to read block-pos list from database: ")
+ (result ? PQresultErrorMessage(result) : "(result was NULL)"));
int rows = PQntuples(result);
// Make sure that we got the right data types
if (rows &&
( PQftype(result, 0) != PG_INT4OID
|| PQftype(result, 1) != PG_INT4OID
|| PQftype(result, 2) != PG_INT4OID)) {
throw std::runtime_error(std::string("Unexpected data type of block coordinate in database query result."));
}
for (int i = 0; i < rows; i++) {
int32_t x = ntohl(*reinterpret_cast<uint32_t *>(PQgetvalue(result, i, 0)));
int32_t y = ntohl(*reinterpret_cast<uint32_t *>(PQgetvalue(result, i, 1)));
int32_t z = ntohl(*reinterpret_cast<uint32_t *>(PQgetvalue(result, i, 2)));
m_blockPosList.push_back(BlockPos(x, y, z, BlockPos::XYZ));
}
PQclear(result);
return m_blockPosList;
}
DB::Block DBPostgreSQL::getBlockOnPos(const BlockPos &pos)
{
Block block(pos,reinterpret_cast<const unsigned char *>(""));
m_blocksQueriedCount++;
for (int i = 0; i < 3; i++) {
m_getBlockParams[i] = htonl(pos.dimension[i]);
}
PGresult *result = PQexecPrepared(m_connection, "GetBlock", 3, m_getBlockParamList, m_getBlockParamLengths, m_getBlockParamFormats, 1);
if (!result || PQresultStatus(result) != PGRES_TUPLES_OK)
throw std::runtime_error(std::string("Failed to read block from database: ")
+ (result ? PQresultErrorMessage(result) : "(result was NULL)"));
if (PQntuples(result) != 0) {
block = Block(pos, ustring(reinterpret_cast<unsigned char *>(PQgetvalue(result, 0, 0)), PQgetlength(result, 0, 0)));
m_blocksReadCount++;
}
PQclear(result);
return block;
}

41
db-postgresql.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef _DB_POSTGRESQL_H
#define _DB_POSTGRESQL_H
#include "db.h"
#include <postgresql/libpq-fe.h>
#if __cplusplus >= 201103L
#include <unordered_map>
#else
#include <map>
#endif
#include <string>
#include <sstream>
#include "types.h"
class DBPostgreSQL : public DB {
#if __cplusplus >= 201103L
typedef std::unordered_map<int64_t, ustring> BlockCache;
#else
typedef std::map<int64_t, ustring> BlockCache;
#endif
public:
DBPostgreSQL(const std::string &mapdir);
virtual int getBlocksQueriedCount(void);
virtual int getBlocksReadCount(void);
virtual const BlockPosList &getBlockPos();
virtual Block getBlockOnPos(const BlockPos &pos);
~DBPostgreSQL();
private:
int m_blocksQueriedCount;
int m_blocksReadCount;
PGconn *m_connection;
BlockPosList m_blockPosList;
uint32_t m_getBlockParams[3];
char const *m_getBlockParamList[3];
int m_getBlockParamLengths[3];
int m_getBlockParamFormats[3];
};
#endif // _DB_POSTGRESQL_H

View File

@ -27,11 +27,12 @@ Libraries
* zlib * zlib
* libgd * libgd
* sqlite3 (optional - enabled by default, set ENABLE_SQLITE3=0 in CMake to disable) * sqlite3 (optional - enabled by default, set ENABLE_SQLITE3=0 in CMake to disable)
* postgresql (optional, set ENABLE_POSTGRESQL=1 in CMake to enable postgresql support)
* leveldb (optional, set ENABLE_LEVELDB=1 in CMake to enable leveldb support) * leveldb (optional, set ENABLE_LEVELDB=1 in CMake to enable leveldb support)
* hiredis (optional, set ENABLE_REDIS=1 in CMake to enable redis support) * hiredis (optional, set ENABLE_REDIS=1 in CMake to enable redis support)
At least one of ``sqlite3``, ``leveldb`` and ``hiredis`` is required. Check the At least one of ``sqlite3``, ``postgresql``, ``leveldb`` and ``hiredis`` is required.
minetest worlds that will be mapped to know which ones should be included. Check the minetest worlds that will be mapped to know which ones should be included.
Build Environment Build Environment
----------------- -----------------
@ -86,11 +87,11 @@ In order to make a ``.deb`` package (if desired), install the required tools:
apt-get install fakeroot apt-get install fakeroot
Finally install the minetestmapper dependencies. At least one of ``libsqlite3-dev``, Finally install the minetestmapper dependencies. At least one of ``libsqlite3-dev``,
``libleveldb-dev`` and ``libhiredis-dev`` is required. ``libpq-dev``, ``libleveldb-dev`` and ``libhiredis-dev`` is required.
:: ::
apt-get install zlib1g-dev libgd-dev libsqlite3-dev libleveldb-dev libhiredis-dev apt-get install zlib1g-dev libgd-dev libsqlite3-dev libpq-dev libleveldb-dev libhiredis-dev
Fedora and Derivatives Fedora and Derivatives
---------------------- ----------------------
@ -120,11 +121,11 @@ In order to make an ``.rpm`` package (if desired), install the required tools:
yum install rpm-build yum install rpm-build
Finally install the minetestmapper dependencies. At least one of ``libsqlite3x-devel``, Finally install the minetestmapper dependencies. At least one of ``libsqlite3x-devel``,
``leveldb-devel`` and ``hiredis-devel`` is required. ``postgresql-devel``, ``leveldb-devel`` and ``hiredis-devel`` is required.
:: ::
yum install zlib-devel gd-devel libsqlite3x-devel leveldb-devel hiredis-devel yum install zlib-devel gd-devel libsqlite3x-devel postgresql-devel leveldb-devel hiredis-devel
Ubuntu Ubuntu
------ ------
@ -193,6 +194,9 @@ CMake Variables
ENABLE_SQLITE3: ENABLE_SQLITE3:
Whether to enable sqlite3 backend support (on by default) Whether to enable sqlite3 backend support (on by default)
ENABLE_POSTGRESQL:
Whether to enable postresql backend support (off by default)
ENABLE_LEVELDB: ENABLE_LEVELDB:
Whether to enable leveldb backend support (off by default) Whether to enable leveldb backend support (off by default)

View File

@ -6,7 +6,7 @@ Minetestmapper generates maps of minetest and freeminer worlds.
Major Features Major Features
============== ==============
* Support for both minetest and freeminer * Support for both minetest and freeminer
* Support for sqlite3, leveldb and redis map databases * Support for sqlite3, postsgresql, leveldb and redis map databases
* Generate a subsection of the map, or a full map * Generate a subsection of the map, or a full map
(but the size of generated images is limited - see (but the size of generated images is limited - see
'Known Problems' below) 'Known Problems' below)

View File

@ -288,7 +288,7 @@ Feedback / information options:
Miscellaneous options Miscellaneous options
..................... .....................
* ``--backend auto|sqlite3|leveldb|redis`` : Specify or override the database backend to use * ``--backend auto|sqlite3|postgresql|leveldb|redis`` : Specify or override the database backend to use
* ``--disable-blocklist-prefetch`` : Do not prefetch a block list - faster when mapping small parts of large worlds. * ``--disable-blocklist-prefetch`` : Do not prefetch a block list - faster when mapping small parts of large worlds.
* ``--database-format minetest-i64|freeminer-axyz|mixed|query`` : Specify the format of the database (needed with --disable-blocklist-prefetch and a leveldb backend). * ``--database-format minetest-i64|freeminer-axyz|mixed|query`` : Specify the format of the database (needed with --disable-blocklist-prefetch and a leveldb backend).
@ -313,8 +313,8 @@ Detailed Description of Options
.. Contents:: :local: .. Contents:: :local:
``--backend auto|sqlite3|leveldb|redis`` ``--backend auto|sqlite3|postgresql|leveldb|redis``
.......................................... ...................................................
Set or override the database backend to use. Set or override the database backend to use.
By default (``auto``), the database is obtained from the world configuration, By default (``auto``), the database is obtained from the world configuration,
@ -1768,7 +1768,7 @@ More information is available:
.. _known problems: features.rst#known-problems .. _known problems: features.rst#known-problems
.. _--backend: `--backend auto\|sqlite3\|leveldb\|redis`_ .. _--backend: `--backend auto\|sqlite3\|postgresql\|leveldb\|redis`_
.. _--bgcolor: `--bgcolor <color>`_ .. _--bgcolor: `--bgcolor <color>`_
.. _--blockcolor: `--blockcolor <color>`_ .. _--blockcolor: `--blockcolor <color>`_
.. _--centergeometry: `--centergeometry <geometry>`_ .. _--centergeometry: `--centergeometry <geometry>`_

View File

@ -6,7 +6,7 @@ map in png format. One world node is rendered as one map pixel.
Features: Features:
- Standard color mapping file included (which maps a node (e.g. default:stone) to the desired color) - Standard color mapping file included (which maps a node (e.g. default:stone) to the desired color)
- Easy creation of custom color mapping files (user-specific, world-specific, command-line) - Easy creation of custom color mapping files (user-specific, world-specific, command-line)
- Supports all minetest backends (sqlite3, leveldb and redis) - Supports all minetest backends (sqlite3, postgresql, leveldb and redis)
- Ability to create a regular map or a height-map - Ability to create a regular map or a height-map
- Ability to draw the map at a reduced scale (up to 1:16) - Ability to draw the map at a reduced scale (up to 1:16)
- Supports both minetest and freeminer worlds - Supports both minetest and freeminer worlds