minetest-mapper-cpp/db-sqlite3.cpp

176 lines
4.9 KiB
C++

#include "db-sqlite3.h"
#include <stdexcept>
#include <unistd.h> // for usleep
#include "types.h"
DBSQLite3::DBSQLite3(const std::string &mapdir) :
cacheWorldRow(false),
m_blocksReadCount(0),
m_blocksCachedCount(0),
m_blocksUnCachedCount(0),
m_blockPosListStatement(NULL),
m_blocksOnZStatement(NULL),
m_blockOnPosStatement(NULL)
{
std::string db_name = mapdir + "map.sqlite";
if (sqlite3_open_v2(db_name.c_str(), &m_db, SQLITE_OPEN_READONLY | SQLITE_OPEN_PRIVATECACHE, 0) != SQLITE_OK) {
throw std::runtime_error(std::string(sqlite3_errmsg(m_db)) + ", Database file: " + db_name);
}
}
DBSQLite3::~DBSQLite3() {
if (m_blockPosListStatement) sqlite3_finalize(m_blockPosListStatement);
if (m_blocksOnZStatement) sqlite3_finalize(m_blocksOnZStatement);
if (m_blockOnPosStatement) sqlite3_finalize(m_blockOnPosStatement);
sqlite3_close(m_db);
}
int DBSQLite3::getBlocksReadCount(void)
{
return m_blocksReadCount;
}
int DBSQLite3::getBlocksCachedCount(void)
{
return m_blocksCachedCount;
}
int DBSQLite3::getBlocksUnCachedCount(void)
{
return m_blocksUnCachedCount;
}
const DB::BlockPosList &DBSQLite3::getBlockPos() {
m_BlockPosList.clear();
std::string sql = "SELECT pos FROM blocks";
if (m_blockPosListStatement || sqlite3_prepare_v2(m_db, sql.c_str(), sql.length(), &m_blockPosListStatement, 0) == SQLITE_OK) {
int result = 0;
while (true) {
result = sqlite3_step(m_blockPosListStatement);
if(result == SQLITE_ROW) {
sqlite3_int64 blocknum = sqlite3_column_int64(m_blockPosListStatement, 0);
m_BlockPosList.push_back(blocknum);
} else if (result == SQLITE_BUSY) // Wait some time and try again
usleep(10000);
else
break;
}
} else {
sqlite3_reset(m_blockPosListStatement);
throw std::runtime_error("Failed to get list of MapBlocks");
}
sqlite3_reset(m_blockPosListStatement);
return m_BlockPosList;
}
void DBSQLite3::prepareBlocksOnZStatement(void)
{
//std::string sql = "SELECT pos, data FROM blocks WHERE (pos >= ? AND pos <= ?)";
std::string sql = "SELECT pos, data FROM blocks WHERE (pos BETWEEN ? AND ?)";
if (!m_blocksOnZStatement && sqlite3_prepare_v2(m_db, sql.c_str(), sql.length(), &m_blocksOnZStatement, 0) != SQLITE_OK) {
throw std::runtime_error("Failed to prepare statement (blocksOnZStatement)");
}
}
void DBSQLite3::prepareBlockOnPosStatement(void)
{
std::string sql = "SELECT pos, data FROM blocks WHERE pos == ?";
if (!m_blockOnPosStatement && sqlite3_prepare_v2(m_db, sql.c_str(), sql.length(), &m_blockOnPosStatement, 0) != SQLITE_OK) {
throw std::runtime_error("Failed to prepare SQL statement (blockOnPosStatement)");
}
}
void DBSQLite3::cacheBlocksOnZRaw(int zPos)
{
prepareBlocksOnZStatement();
sqlite3_int64 psMin;
sqlite3_int64 psMax;
psMin = (static_cast<sqlite3_int64>(zPos) * 0x1000000L) - 0x800000;
psMax = (static_cast<sqlite3_int64>(zPos) * 0x1000000L) + 0x7fffff;
sqlite3_bind_int64(m_blocksOnZStatement, 1, psMin);
sqlite3_bind_int64(m_blocksOnZStatement, 2, psMax);
cacheBlocks(m_blocksOnZStatement);
}
DB::Block DBSQLite3::getBlockOnPosRaw(const BlockPos &pos)
{
prepareBlockOnPosStatement();
Block block(pos,reinterpret_cast<const unsigned char *>(""));
int result = 0;
sqlite3_bind_int64(m_blockOnPosStatement, 1, pos.databasePos());
while (true) {
result = sqlite3_step(m_blockOnPosStatement);
if(result == SQLITE_ROW) {
const unsigned char *data = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(m_blockOnPosStatement, 1));
int size = sqlite3_column_bytes(m_blockOnPosStatement, 1);
block = Block(pos, ustring(data, size));
m_blocksUnCachedCount++;
break;
} else if (result == SQLITE_BUSY) { // Wait some time and try again
usleep(10000);
} else {
break;
}
}
sqlite3_reset(m_blockOnPosStatement);
return block;
}
void DBSQLite3::cacheBlocks(sqlite3_stmt *SQLstatement)
{
int result = 0;
while (true) {
result = sqlite3_step(SQLstatement);
if(result == SQLITE_ROW) {
sqlite3_int64 blocknum = sqlite3_column_int64(SQLstatement, 0);
const unsigned char *data = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(SQLstatement, 1));
int size = sqlite3_column_bytes(SQLstatement, 1);
m_blockCache[blocknum] = ustring(data, size);
m_blocksCachedCount++;
} else if (result == SQLITE_BUSY) { // Wait some time and try again
usleep(10000);
} else {
break;
}
}
sqlite3_reset(SQLstatement);
}
DB::Block DBSQLite3::getBlockOnPos(const BlockPos &pos)
{
m_blocksReadCount++;
BlockCache::const_iterator DBBlockSearch;
DBBlockSearch = m_blockCache.find(pos.databasePos());
if (DBBlockSearch == m_blockCache.end()) {
if (cacheWorldRow) {
m_blockCache.clear();
cacheBlocksOnZRaw(pos.z);
DBBlockSearch = m_blockCache.find(pos.databasePos());
if (DBBlockSearch != m_blockCache.end()) {
return Block(pos, DBBlockSearch->second);
}
else {
return Block(pos,reinterpret_cast<const unsigned char *>(""));
}
}
else {
return getBlockOnPosRaw(pos);
}
}
else {
return Block(pos, DBBlockSearch->second);
}
}