summaryrefslogtreecommitdiff
path: root/src/database-sqlite3.cpp
diff options
context:
space:
mode:
authorShadowNinja <shadowninja@minetest.net>2014-11-16 15:31:57 -0500
committerShadowNinja <shadowninja@minetest.net>2015-03-06 00:20:45 -0500
commit708337dfc2b3871dc6de983e781e4a4a60a1881d (patch)
tree97352d676d9d76b036d8b559ae4d3c6fe83bbf12 /src/database-sqlite3.cpp
parentc7454d4732dee0f7364ccb8e07002df1a037b94d (diff)
downloadminetest-708337dfc2b3871dc6de983e781e4a4a60a1881d.tar.gz
minetest-708337dfc2b3871dc6de983e781e4a4a60a1881d.tar.bz2
minetest-708337dfc2b3871dc6de983e781e4a4a60a1881d.zip
Clean up database API and save the local map on an interval
Diffstat (limited to 'src/database-sqlite3.cpp')
-rw-r--r--src/database-sqlite3.cpp337
1 files changed, 136 insertions, 201 deletions
diff --git a/src/database-sqlite3.cpp b/src/database-sqlite3.cpp
index 2edae8be2..3480894c9 100644
--- a/src/database-sqlite3.cpp
+++ b/src/database-sqlite3.cpp
@@ -18,259 +18,205 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
/*
- SQLite format specification:
- - Initially only replaces sectors/ and sectors2/
-
- If map.sqlite does not exist in the save dir
- or the block was not found in the database
- the map will try to load from sectors folder.
- In either case, map.sqlite will be created
- and all future saves will save there.
-
- Structure of map.sqlite:
- Tables:
- blocks
- (PK) INT pos
- BLOB data
+SQLite format specification:
+ blocks:
+ (PK) INT id
+ BLOB data
*/
#include "database-sqlite3.h"
-#include "map.h"
-#include "mapsector.h"
-#include "mapblock.h"
-#include "serialization.h"
-#include "main.h"
-#include "settings.h"
#include "log.h"
#include "filesys.h"
+#include "exceptions.h"
+#include "main.h"
+#include "settings.h"
+#include "util/string.h"
+
+#include <cassert>
+
+
+#define SQLRES(s, r) \
+ if ((s) != (r)) { \
+ throw FileNotGoodException(std::string(\
+ "SQLite3 database error (" \
+ __FILE__ ":" TOSTRING(__LINE__) \
+ "): ") +\
+ sqlite3_errmsg(m_database)); \
+ }
+#define SQLOK(s) SQLRES(s, SQLITE_OK)
+
+#define PREPARE_STATEMENT(name, query) \
+ SQLOK(sqlite3_prepare_v2(m_database, query, -1, &m_stmt_##name, NULL))
+
+#define FINALIZE_STATEMENT(statement) \
+ if (sqlite3_finalize(statement) != SQLITE_OK) { \
+ throw FileNotGoodException(std::string( \
+ "SQLite3: Failed to finalize " #statement ": ") + \
+ sqlite3_errmsg(m_database)); \
+ }
-Database_SQLite3::Database_SQLite3(ServerMap *map, std::string savedir)
-{
- m_database = NULL;
- m_database_read = NULL;
- m_database_write = NULL;
- m_database_list = NULL;
- m_database_delete = NULL;
- m_savedir = savedir;
- srvmap = map;
-}
-int Database_SQLite3::Initialized(void)
+Database_SQLite3::Database_SQLite3(const std::string &savedir) :
+ m_initialized(false),
+ m_savedir(savedir),
+ m_database(NULL),
+ m_stmt_read(NULL),
+ m_stmt_write(NULL),
+ m_stmt_list(NULL),
+ m_stmt_delete(NULL)
{
- return m_database ? 1 : 0;
}
void Database_SQLite3::beginSave() {
verifyDatabase();
- if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
- errorstream<<"WARNING: beginSave() failed, saving might be slow.";
+ SQLRES(sqlite3_step(m_stmt_begin), SQLITE_DONE);
+ sqlite3_reset(m_stmt_begin);
}
void Database_SQLite3::endSave() {
verifyDatabase();
- if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
- errorstream<<"WARNING: endSave() failed, map might not have saved.";
+ SQLRES(sqlite3_step(m_stmt_end), SQLITE_DONE);
+ sqlite3_reset(m_stmt_end);
}
-void Database_SQLite3::createDirs(std::string path)
+void Database_SQLite3::openDatabase()
{
- if(fs::CreateAllDirs(path) == false)
- {
- infostream<<DTIME<<"Database_SQLite3: Failed to create directory "
- <<"\""<<path<<"\""<<std::endl;
- throw BaseException("Database_SQLite3 failed to create directory");
- }
-}
+ if (m_database) return;
-void Database_SQLite3::verifyDatabase() {
- if(m_database)
- return;
-
- std::string dbp = m_savedir + DIR_DELIM "map.sqlite";
- bool needs_create = false;
- int d;
+ std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
// Open the database connection
- createDirs(m_savedir); // ?
+ if (!fs::CreateAllDirs(m_savedir)) {
+ infostream << "Database_SQLite3: Failed to create directory \""
+ << m_savedir << "\"" << std::endl;
+ throw FileNotGoodException("Failed to create database "
+ "save directory");
+ }
- if(!fs::PathExists(dbp))
- needs_create = true;
+ bool needs_create = !fs::PathExists(dbp);
- d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
- if(d != SQLITE_OK) {
- errorstream<<"SQLite3 database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
+ if (sqlite3_open_v2(dbp.c_str(), &m_database,
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
+ NULL) != SQLITE_OK) {
+ errorstream << "SQLite3 database failed to open: "
+ << sqlite3_errmsg(m_database) << std::endl;
throw FileNotGoodException("Cannot open database file");
}
- if(needs_create)
+ if (needs_create) {
createDatabase();
+ }
- std::string querystr = std::string("PRAGMA synchronous = ")
+ std::string query_str = std::string("PRAGMA synchronous = ")
+ itos(g_settings->getU16("sqlite_synchronous"));
- d = sqlite3_exec(m_database, querystr.c_str(), NULL, NULL, NULL);
- if(d != SQLITE_OK) {
- errorstream<<"Database pragma set failed: "
- <<sqlite3_errmsg(m_database)<<std::endl;
- throw FileNotGoodException("Cannot set pragma");
- }
+ SQLOK(sqlite3_exec(m_database, query_str.c_str(), NULL, NULL, NULL));
+}
- d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
- if(d != SQLITE_OK) {
- errorstream<<"SQLite3 read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
- throw FileNotGoodException("Cannot prepare read statement");
- }
+void Database_SQLite3::verifyDatabase()
+{
+ if (m_initialized) return;
+
+ openDatabase();
+
+ PREPARE_STATEMENT(begin, "BEGIN");
+ PREPARE_STATEMENT(end, "COMMIT");
+ PREPARE_STATEMENT(read, "SELECT `data` FROM `blocks` WHERE `pos` = ? LIMIT 1");
#ifdef __ANDROID__
- d = sqlite3_prepare(m_database, "INSERT INTO `blocks` VALUES(?, ?);", -1, &m_database_write, NULL);
+ PREPARE_STATEMENT(write, "INSERT INTO `blocks` (`pos`, `data`) VALUES (?, ?)");
#else
- d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?);", -1, &m_database_write, NULL);
+ PREPARE_STATEMENT(write, "REPLACE INTO `blocks` (`pos`, `data`) VALUES (?, ?)");
#endif
- if(d != SQLITE_OK) {
- errorstream<<"SQLite3 write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
- throw FileNotGoodException("Cannot prepare write statement");
- }
+ PREPARE_STATEMENT(delete, "DELETE FROM `blocks` WHERE `pos` = ?");
+ PREPARE_STATEMENT(list, "SELECT `pos` FROM `blocks`");
- d = sqlite3_prepare(m_database, "DELETE FROM `blocks` WHERE `pos`=?;", -1, &m_database_delete, NULL);
- if(d != SQLITE_OK) {
- infostream<<"WARNING: SQLite3 database delete statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
- throw FileNotGoodException("Cannot prepare delete statement");
- }
+ m_initialized = true;
- d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
- if(d != SQLITE_OK) {
- infostream<<"SQLite3 list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
- throw FileNotGoodException("Cannot prepare read statement");
- }
+ verbosestream << "ServerMap: SQLite3 database opened." << std::endl;
+}
- infostream<<"ServerMap: SQLite3 database opened"<<std::endl;
+inline void Database_SQLite3::bindPos(sqlite3_stmt *stmt, const v3s16 &pos, int index)
+{
+ SQLOK(sqlite3_bind_int64(stmt, index, getBlockAsInteger(pos)));
}
-bool Database_SQLite3::deleteBlock(v3s16 blockpos)
+bool Database_SQLite3::deleteBlock(const v3s16 &pos)
{
verifyDatabase();
- if (sqlite3_bind_int64(m_database_delete, 1,
- getBlockAsInteger(blockpos)) != SQLITE_OK) {
- errorstream << "WARNING: Could not bind block position for delete: "
- << sqlite3_errmsg(m_database) << std::endl;
- }
+ bindPos(m_stmt_delete, pos);
+
+ bool good = sqlite3_step(m_stmt_delete) == SQLITE_DONE;
+ sqlite3_reset(m_stmt_delete);
- if (sqlite3_step(m_database_delete) != SQLITE_DONE) {
+ if (!good) {
errorstream << "WARNING: deleteBlock: Block failed to delete "
- << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
- sqlite3_reset(m_database_delete);
- return false;
+ << PP(pos) << ": " << sqlite3_errmsg(m_database) << std::endl;
}
-
- sqlite3_reset(m_database_delete);
- return true;
+ return good;
}
-bool Database_SQLite3::saveBlock(v3s16 blockpos, std::string &data)
+bool Database_SQLite3::saveBlock(const v3s16 &pos, const std::string &data)
{
verifyDatabase();
- s64 bkey = getBlockAsInteger(blockpos);
-
#ifdef __ANDROID__
/**
- * Note: For some unknown reason sqlite3 fails to REPLACE blocks on android,
- * deleting them and inserting first works.
+ * Note: For some unknown reason SQLite3 fails to REPLACE blocks on Android,
+ * deleting them and then inserting works.
*/
- if (sqlite3_bind_int64(m_database_read, 1, bkey) != SQLITE_OK) {
- infostream << "WARNING: Could not bind block position for load: "
- << sqlite3_errmsg(m_database)<<std::endl;
- }
+ bindPos(m_stmt_read, pos);
- int step_result = sqlite3_step(m_database_read);
- sqlite3_reset(m_database_read);
-
- if (step_result == SQLITE_ROW) {
- if (sqlite3_bind_int64(m_database_delete, 1, bkey) != SQLITE_OK) {
- infostream << "WARNING: Could not bind block position for delete: "
- << sqlite3_errmsg(m_database)<<std::endl;
- }
-
- if (sqlite3_step(m_database_delete) != SQLITE_DONE) {
- errorstream << "WARNING: saveBlock: Block failed to delete "
- << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
- return false;
- }
- sqlite3_reset(m_database_delete);
+ if (sqlite3_step(m_stmt_read) == SQLITE_ROW) {
+ deleteBlock(pos);
}
+ sqlite3_reset(m_stmt_read);
#endif
- if (sqlite3_bind_int64(m_database_write, 1, bkey) != SQLITE_OK) {
- errorstream << "WARNING: saveBlock: Block position failed to bind: "
- << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
- sqlite3_reset(m_database_write);
- return false;
- }
-
- if (sqlite3_bind_blob(m_database_write, 2, (void *)data.c_str(),
- data.size(), NULL) != SQLITE_OK) {
- errorstream << "WARNING: saveBlock: Block data failed to bind: "
- << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
- sqlite3_reset(m_database_write);
- return false;
- }
-
- if (sqlite3_step(m_database_write) != SQLITE_DONE) {
- errorstream << "WARNING: saveBlock: Block failed to save "
- << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
- sqlite3_reset(m_database_write);
- return false;
- }
+ bindPos(m_stmt_write, pos);
+ SQLOK(sqlite3_bind_blob(m_stmt_write, 2, data.data(), data.size(), NULL));
- sqlite3_reset(m_database_write);
+ SQLRES(sqlite3_step(m_stmt_write), SQLITE_DONE)
+ sqlite3_reset(m_stmt_write);
return true;
}
-std::string Database_SQLite3::loadBlock(v3s16 blockpos)
+std::string Database_SQLite3::loadBlock(const v3s16 &pos)
{
verifyDatabase();
- if (sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK) {
- errorstream << "Could not bind block position for load: "
- << sqlite3_errmsg(m_database)<<std::endl;
- }
+ bindPos(m_stmt_read, pos);
- if (sqlite3_step(m_database_read) == SQLITE_ROW) {
- const char *data = (const char *) sqlite3_column_blob(m_database_read, 0);
- size_t len = sqlite3_column_bytes(m_database_read, 0);
-
- std::string s = "";
- if(data)
- s = std::string(data, len);
+ if (sqlite3_step(m_stmt_read) != SQLITE_ROW) {
+ sqlite3_reset(m_stmt_read);
+ return "";
+ }
+ const char *data = (const char *) sqlite3_column_blob(m_stmt_read, 0);
+ size_t len = sqlite3_column_bytes(m_stmt_read, 0);
- sqlite3_step(m_database_read);
- // We should never get more than 1 row, so ok to reset
- sqlite3_reset(m_database_read);
+ std::string s;
+ if (data)
+ s = std::string(data, len);
- return s;
- }
+ sqlite3_step(m_stmt_read);
+ // We should never get more than 1 row, so ok to reset
+ sqlite3_reset(m_stmt_read);
- sqlite3_reset(m_database_read);
- return "";
+ return s;
}
void Database_SQLite3::createDatabase()
{
- int e;
assert(m_database);
- e = sqlite3_exec(m_database,
- "CREATE TABLE IF NOT EXISTS `blocks` ("
- "`pos` INT NOT NULL PRIMARY KEY,"
- "`data` BLOB"
- ");"
- , NULL, NULL, NULL);
- if(e != SQLITE_OK)
- throw FileNotGoodException("Could not create sqlite3 database structure");
- else
- infostream<<"ServerMap: SQLite3 database structure was created";
+ SQLOK(sqlite3_exec(m_database,
+ "CREATE TABLE IF NOT EXISTS `blocks` (\n"
+ " `pos` INT PRIMARY KEY,\n"
+ " `data` BLOB\n"
+ ");\n",
+ NULL, NULL, NULL));
}
@@ -278,36 +224,25 @@ void Database_SQLite3::listAllLoadableBlocks(std::vector<v3s16> &dst)
{
verifyDatabase();
- while(sqlite3_step(m_database_list) == SQLITE_ROW) {
- sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
- v3s16 p = getIntegerAsBlock(block_i);
- //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
- dst.push_back(p);
+ while (sqlite3_step(m_stmt_list) == SQLITE_ROW) {
+ dst.push_back(getIntegerAsBlock(sqlite3_column_int64(m_stmt_list, 0)));
}
+ sqlite3_reset(m_stmt_list);
}
-
-#define FINALIZE_STATEMENT(statement) \
- if ( statement ) \
- rc = sqlite3_finalize(statement); \
- if ( rc != SQLITE_OK ) \
- errorstream << "Database_SQLite3::~Database_SQLite3():" \
- << "Failed to finalize: " << #statement << ": rc=" << rc << std::endl;
-
Database_SQLite3::~Database_SQLite3()
{
- int rc = SQLITE_OK;
-
- FINALIZE_STATEMENT(m_database_read)
- FINALIZE_STATEMENT(m_database_write)
- FINALIZE_STATEMENT(m_database_list)
- FINALIZE_STATEMENT(m_database_delete)
-
- if(m_database)
- rc = sqlite3_close(m_database);
-
- if (rc != SQLITE_OK) {
+ FINALIZE_STATEMENT(m_stmt_read)
+ FINALIZE_STATEMENT(m_stmt_write)
+ FINALIZE_STATEMENT(m_stmt_list)
+ FINALIZE_STATEMENT(m_stmt_begin)
+ FINALIZE_STATEMENT(m_stmt_end)
+ FINALIZE_STATEMENT(m_stmt_delete)
+
+ if (sqlite3_close(m_database) != SQLITE_OK) {
errorstream << "Database_SQLite3::~Database_SQLite3(): "
- << "Failed to close database: rc=" << rc << std::endl;
+ << "Failed to close database: "
+ << sqlite3_errmsg(m_database) << std::endl;
}
}
+