Skip to content

Commit

Permalink
Split blockpos into three columns in sqlite3 map database
Browse files Browse the repository at this point in the history
  • Loading branch information
sfan5 committed Feb 8, 2025
1 parent 7cd9442 commit d377284
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 17 deletions.
103 changes: 88 additions & 15 deletions src/database/database-sqlite3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,43 @@ void Database_SQLite3::verifyDatabase()
m_initialized = true;
}

bool Database_SQLite3::checkTable(const char *table)
{
assert(m_database);

sqlite3_stmt *m_stmt_tmp = nullptr;
PREPARE_STATEMENT(tmp, "PRAGMA table_list;");

bool ret = false;
while (sqlite3_step(m_stmt_tmp) == SQLITE_ROW) {
ret |= sqlite_to_string_view(m_stmt_tmp, 1) == table;
if (ret)
break;
}

FINALIZE_STATEMENT(tmp)
return ret;
}

bool Database_SQLite3::checkColumn(const char *table, const char *column)
{
assert(m_database);

sqlite3_stmt *m_stmt_tmp = nullptr;
auto query_str = std::string("PRAGMA table_info(").append(table).append(");");
PREPARE_STATEMENT(tmp, query_str.c_str());

bool ret = false;
while (sqlite3_step(m_stmt_tmp) == SQLITE_ROW) {
ret |= sqlite_to_string_view(m_stmt_tmp, 1) == column;
if (ret)
break;
}

FINALIZE_STATEMENT(tmp)
return ret;
}

Database_SQLite3::~Database_SQLite3()
{
FINALIZE_STATEMENT(begin)
Expand Down Expand Up @@ -198,26 +235,53 @@ void MapDatabaseSQLite3::createDatabase()
{
assert(m_database);

SQLOK(sqlite3_exec(m_database,
// Note: before 5.12.0 the format was blocks(pos INT, data BLOB).
// This function only runs for fresh databases so we don't mind it here.

const char *schema =
"CREATE TABLE IF NOT EXISTS `blocks` (\n"
" `pos` INT PRIMARY KEY,\n"
" `data` BLOB\n"
");\n",
NULL, NULL, NULL),
"`x` INT,"
"`y` INT,"
"`z` INT,"
"`data` BLOB,"
"PRIMARY KEY (`x`, `y`, `z`)"
");\n"
;
SQLOK(sqlite3_exec(m_database, schema, NULL, NULL, NULL),
"Failed to create database table");
}

void MapDatabaseSQLite3::initStatements()
{
PREPARE_STATEMENT(read, "SELECT `data` FROM `blocks` WHERE `pos` = ? LIMIT 1");
PREPARE_STATEMENT(write, "REPLACE INTO `blocks` (`pos`, `data`) VALUES (?, ?)");
PREPARE_STATEMENT(delete, "DELETE FROM `blocks` WHERE `pos` = ?");
PREPARE_STATEMENT(list, "SELECT `pos` FROM `blocks`");
assert(checkTable("blocks"));
m_new_format = checkColumn("blocks", "z");
infostream << "MapDatabaseSQLite3: split column format = "
<< (m_new_format ? "yes" : "no") << std::endl;

if (m_new_format) {
PREPARE_STATEMENT(read, "SELECT `data` FROM `blocks` WHERE `x` = ? AND `y` = ? AND `z` = ? LIMIT 1");
PREPARE_STATEMENT(write, "REPLACE INTO `blocks` (`x`, `y`, `z`, `data`) VALUES (?, ?, ?, ?)");
PREPARE_STATEMENT(delete, "DELETE FROM `blocks` WHERE `x` = ? AND `y` = ? AND `z` = ?");
PREPARE_STATEMENT(list, "SELECT `x`, `y`, `z` FROM `blocks`");
} else {
PREPARE_STATEMENT(read, "SELECT `data` FROM `blocks` WHERE `pos` = ? LIMIT 1");
PREPARE_STATEMENT(write, "REPLACE INTO `blocks` (`pos`, `data`) VALUES (?, ?)");
PREPARE_STATEMENT(delete, "DELETE FROM `blocks` WHERE `pos` = ?");
PREPARE_STATEMENT(list, "SELECT `pos` FROM `blocks`");
}
}

inline void MapDatabaseSQLite3::bindPos(sqlite3_stmt *stmt, const v3s16 &pos, int index)
inline int MapDatabaseSQLite3::bindPos(sqlite3_stmt *stmt, v3s16 pos, int index)
{
int64_to_sqlite(stmt, index, getBlockAsInteger(pos));
if (m_new_format) {
int_to_sqlite(stmt, index, pos.X);
int_to_sqlite(stmt, index + 1, pos.Y);
int_to_sqlite(stmt, index + 2, pos.Z);
return index + 3;
} else {
int64_to_sqlite(stmt, index, getBlockAsInteger(pos));
return index + 1;
}
}

bool MapDatabaseSQLite3::deleteBlock(const v3s16 &pos)
Expand All @@ -240,8 +304,8 @@ bool MapDatabaseSQLite3::saveBlock(const v3s16 &pos, std::string_view data)
{
verifyDatabase();

bindPos(m_stmt_write, pos);
str_to_sqlite(m_stmt_write, 2, data);
int col = bindPos(m_stmt_write, pos);
str_to_sqlite(m_stmt_write, col, data);

SQLRES(sqlite3_step(m_stmt_write), SQLITE_DONE, "Failed to save block")
sqlite3_reset(m_stmt_write);
Expand Down Expand Up @@ -271,8 +335,17 @@ void MapDatabaseSQLite3::listAllLoadableBlocks(std::vector<v3s16> &dst)
{
verifyDatabase();

while (sqlite3_step(m_stmt_list) == SQLITE_ROW)
dst.push_back(getIntegerAsBlock(sqlite3_column_int64(m_stmt_list, 0)));
v3s16 p;
while (sqlite3_step(m_stmt_list) == SQLITE_ROW) {
if (m_new_format) {
p.X = sqlite_to_int(m_stmt_list, 0);
p.Y = sqlite_to_int(m_stmt_list, 1);
p.Z = sqlite_to_int(m_stmt_list, 2);
} else {
p = getIntegerAsBlock(sqlite_to_int64(m_stmt_list, 0));
}
dst.push_back(p);
}

sqlite3_reset(m_stmt_list);
}
Expand Down
13 changes: 11 additions & 2 deletions src/database/database-sqlite3.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ class Database_SQLite3 : public Database
// Open and initialize the database if needed (not thread-safe)
void verifyDatabase();

// Check if a specific table exists
bool checkTable(const char *table);

// Check if a table has a specific column
bool checkColumn(const char *table, const char *column);

/* Value conversion helpers */

inline void str_to_sqlite(sqlite3_stmt *s, int iCol, std::string_view str) const
Expand Down Expand Up @@ -167,9 +173,12 @@ class MapDatabaseSQLite3 : private Database_SQLite3, public MapDatabase
virtual void initStatements();

private:
void bindPos(sqlite3_stmt *stmt, const v3s16 &pos, int index = 1);
/// @brief Bind block position into statement at column index
/// @return index of next column after position
int bindPos(sqlite3_stmt *stmt, v3s16 pos, int index = 1);

bool m_new_format = false;

// Map
sqlite3_stmt *m_stmt_read = nullptr;
sqlite3_stmt *m_stmt_write = nullptr;
sqlite3_stmt *m_stmt_list = nullptr;
Expand Down

0 comments on commit d377284

Please sign in to comment.