2018-03-23 04:34:52 -07:00
2018-04-24 08:47:05 -07:00
# include "db-postgresql.h"
2018-03-23 04:34:52 -07:00
2018-04-24 08:47:05 -07:00
# ifdef USE_POSTGRESQL
2018-03-23 04:34:52 -07:00
2015-12-16 06:15:47 -08:00
# include <stdexcept>
2018-04-24 08:47:05 -07:00
# if _WIN32
# include <Winsock2.h> // htonl
# else
# include <arpa/inet.h> // htonl
# endif
2015-12-16 06:15:47 -08:00
# include "Settings.h"
2016-05-17 08:33:02 -07:00
# define BLOCKPOSLIST_QUERY_COMPAT "SELECT x, y, z FROM blocks"
# define BLOCKPOSLISTBOUNDED_QUERY_COMPAT "SELECT x, y, z FROM blocks WHERE x BETWEEN $1 AND $2 AND y BETWEEN $3 AND $4 AND z BETWEEN $5 AND $6"
# define BLOCK_QUERY_COMPAT "SELECT data FROM blocks WHERE x = $1 AND y = $2 AND z = $3"
# define BLOCKPOSLIST_QUERY "SELECT posX, posY, posZ FROM blocks"
# define BLOCKPOSLISTBOUNDED_QUERY "SELECT posX, posY, posZ FROM blocks WHERE posX BETWEEN $1 AND $2 AND posY BETWEEN $3 AND $4 AND posZ BETWEEN $5 AND $6"
# define BLOCK_QUERY "SELECT data FROM blocks WHERE posX = $1 AND posY = $2 AND posZ = $3"
2015-12-16 06:15:47 -08:00
// 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 ;
2016-05-17 08:33:02 -07:00
bool compat_mode = false ;
// Official postgresql connection info string
info_found = world_mt . check ( " pgsql_connection " , connection_info ) ;
compat_mode = ! info_found ;
// ShadowNinja's implementation (historical)
if ( ! info_found )
info_found = world_mt . check ( " postgresql_connection_info " , connection_info ) ;
// johnnyjoy's implementation (historical)
2015-12-16 06:15:47 -08:00
if ( ! info_found )
info_found = world_mt . check ( " pg_connection_info " , connection_info ) ;
2016-05-17 08:33:02 -07:00
2015-12-16 06:15:47 -08:00
if ( ! info_found )
2016-05-17 08:33:02 -07:00
throw std : : runtime_error ( " Set pgsql_connection in world.mt to use the postgresql backend " ) ;
2015-12-16 06:15:47 -08:00
2015-12-16 10:16:22 -08:00
connection_info + = " fallback_application_name=minetestmapper " + connection_info ;
2015-12-16 06:15:47 -08:00
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 ) ) ;
}
2016-05-17 08:33:02 -07:00
const char * blockposlist_query = BLOCKPOSLIST_QUERY ;
const char * blockposlistbounded_query = BLOCKPOSLISTBOUNDED_QUERY ;
const char * block_query = BLOCK_QUERY ;
if ( compat_mode ) {
blockposlist_query = BLOCKPOSLIST_QUERY_COMPAT ;
blockposlistbounded_query = BLOCKPOSLISTBOUNDED_QUERY_COMPAT ;
block_query = BLOCK_QUERY_COMPAT ;
}
2015-12-16 06:15:47 -08:00
PGresult * result ;
2015-12-17 02:54:03 -08:00
2016-05-17 08:33:02 -07:00
result = PQprepare ( m_connection , " GetBlockPosList " , blockposlist_query , 0 , NULL ) ;
2015-12-16 06:15:47 -08:00
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 ) ;
2016-05-17 08:33:02 -07:00
result = PQprepare ( m_connection , " GetBlockPosListBounded " , blockposlistbounded_query , 0 , NULL ) ;
2015-12-17 02:54:03 -08:00
if ( ! result | | PQresultStatus ( result ) ! = PGRES_COMMAND_OK )
throw std : : runtime_error ( std : : string ( " Failed to prepare PostgreSQL statement (GetBlockPosListBounded): " )
+ ( result ? PQresultErrorMessage ( result ) : " (result was NULL) " ) ) ;
PQclear ( result ) ;
2016-05-17 08:33:02 -07:00
result = PQprepare ( m_connection , " GetBlock " , block_query , 0 , NULL ) ;
2015-12-16 06:15:47 -08:00
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 ) ;
2015-12-17 02:54:03 -08:00
for ( int i = 0 ; i < POSTGRESQL_MAXPARAMS ; i + + ) {
2015-12-16 06:15:47 -08:00
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 ;
}
2015-12-17 02:54:03 -08:00
const DB : : BlockPosList & DBPostgreSQL : : getBlockPosList ( )
{
PGresult * result = PQexecPrepared ( m_connection , " GetBlockPosList " , 0 , NULL , NULL , NULL , 1 ) ;
return processBlockPosListQueryResult ( result ) ;
}
const DB : : BlockPosList & DBPostgreSQL : : getBlockPosList ( BlockPos minPos , BlockPos maxPos )
{
for ( int i = 0 ; i < 3 ; i + + ) {
m_getBlockParams [ 2 * i ] = htonl ( minPos . dimension [ i ] ) ;
m_getBlockParams [ 2 * i + 1 ] = htonl ( maxPos . dimension [ i ] ) ;
}
PGresult * result = PQexecPrepared ( m_connection , " GetBlockPosListBounded " , 6 , m_getBlockParamList , m_getBlockParamLengths , m_getBlockParamFormats , 1 ) ;
return processBlockPosListQueryResult ( result ) ;
}
const DB : : BlockPosList & DBPostgreSQL : : processBlockPosListQueryResult ( PGresult * result ) {
2015-12-16 06:15:47 -08:00
m_blockPosList . clear ( ) ;
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 ;
}
2018-04-24 08:47:05 -07:00
const DB : : Block DBPostgreSQL : : getBlockOnPos ( const BlockPos & pos )
2015-12-16 06:15:47 -08:00
{
2018-04-24 08:47:05 -07:00
Block block ( pos , { } ) ;
2015-12-16 06:15:47 -08:00
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 ) {
2018-04-24 08:47:05 -07:00
block = Block ( pos , reinterpret_cast < unsigned char * > ( PQgetvalue ( result , 0 , 0 ) ) , PQgetlength ( result , 0 , 0 ) ) ;
2015-12-16 06:15:47 -08:00
m_blocksReadCount + + ;
}
PQclear ( result ) ;
return block ;
}
2018-03-23 04:34:52 -07:00
# endif // USE_POSTGRESQL