minetest-mapper-cpp/db-sqlite3.cpp
Rogier 80a4e4b665 Fix blocks not found while minetest is running, using SQLite3 backend
If mapping while minetest is running and modifying blocks, modified
blocks queried by RowID, would not be found by SQLite3. Querying
by I64 exclusively did not cause such behavior.
2016-05-30 13:09:29 +02:00

109 lines
3.4 KiB
C++

#include "db-sqlite3.h"
#include <stdexcept>
#include <unistd.h> // for usleep
#include "types.h"
#define BLOCKPOSLIST_STATEMENT "SELECT pos, rowid FROM blocks"
#define BLOCK_STATEMENT_POS "SELECT pos, data FROM blocks WHERE pos == ?"
#define BLOCK_STATEMENT_ROWID "SELECT pos, data FROM blocks WHERE rowid == ?"
DBSQLite3::DBSQLite3(const std::string &mapdir) :
m_blocksQueriedCount(0),
m_blocksReadCount(0),
m_blockPosListStatement(NULL),
m_blockOnPosStatement(NULL),
m_blockOnRowidStatement(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);
}
if (SQLITE_OK != sqlite3_prepare_v2(m_db, BLOCKPOSLIST_STATEMENT, sizeof(BLOCKPOSLIST_STATEMENT)-1, &m_blockPosListStatement, 0)) {
throw std::runtime_error("Failed to prepare SQL statement (blockPosListStatement)");
}
if (SQLITE_OK != sqlite3_prepare_v2(m_db, BLOCK_STATEMENT_POS, sizeof(BLOCK_STATEMENT_POS)-1, &m_blockOnPosStatement, 0)) {
throw std::runtime_error("Failed to prepare SQL statement (blockOnPosStatement)");
}
if (SQLITE_OK != sqlite3_prepare_v2(m_db, BLOCK_STATEMENT_ROWID, sizeof(BLOCK_STATEMENT_ROWID)-1, &m_blockOnRowidStatement, 0)) {
throw std::runtime_error("Failed to prepare SQL statement (blockOnRowidStatement)");
}
}
DBSQLite3::~DBSQLite3() {
if (m_blockPosListStatement) sqlite3_finalize(m_blockPosListStatement);
if (m_blockOnPosStatement) sqlite3_finalize(m_blockOnPosStatement);
if (m_blockOnRowidStatement) sqlite3_finalize(m_blockOnRowidStatement);
sqlite3_close(m_db);
}
int DBSQLite3::getBlocksReadCount(void)
{
return m_blocksReadCount;
}
int DBSQLite3::getBlocksQueriedCount(void)
{
return m_blocksQueriedCount;
}
const DB::BlockPosList &DBSQLite3::getBlockPosList() {
m_BlockPosList.clear();
int result = 0;
while (true) {
result = sqlite3_step(m_blockPosListStatement);
if(result == SQLITE_ROW) {
sqlite3_int64 blocknum = sqlite3_column_int64(m_blockPosListStatement, 0);
sqlite3_int64 rowid = sqlite3_column_int64(m_blockPosListStatement, 1);
m_BlockPosList.push_back(BlockPos(blocknum, rowid));
} else if (result == SQLITE_BUSY) // Wait some time and try again
usleep(10000);
else
break;
}
sqlite3_reset(m_blockPosListStatement);
return m_BlockPosList;
}
DB::Block DBSQLite3::getBlockOnPos(const BlockPos &pos)
{
Block block(pos,reinterpret_cast<const unsigned char *>(""));
int result = 0;
m_blocksQueriedCount++;
sqlite3_stmt *statement;
// Disabled RowID querying, as it may cause blocks not to be found when mapping
// while minetest is running (i.e. modifying blocks).
if (0 && pos.databasePosIdIsValid()) {
statement = m_blockOnRowidStatement;
sqlite3_bind_int64(m_blockOnRowidStatement, 1, pos.databasePosId());
}
else {
statement = m_blockOnPosStatement;
sqlite3_bind_int64(m_blockOnPosStatement, 1, pos.databasePosI64());
}
while (true) {
result = sqlite3_step(statement);
if(result == SQLITE_ROW) {
const unsigned char *data = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(statement, 1));
int size = sqlite3_column_bytes(statement, 1);
block = Block(pos, ustring(data, size));
m_blocksReadCount++;
break;
} else if (result == SQLITE_BUSY) { // Wait some time and try again
usleep(10000);
} else {
break;
}
}
sqlite3_reset(statement);
return block;
}