]> git.lizzy.rs Git - minetest.git/blobdiff - src/database-sqlite3.cpp
Fixes #1687 by extra semaphore retval handle code for OSX
[minetest.git] / src / database-sqlite3.cpp
index 7f7850719ee4e0116986dcbf5ddd6f3247c3f0d1..8e1501786b84294da68a6f1feb79015f2c7a18dd 100644 (file)
@@ -44,6 +44,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "main.h"
 #include "settings.h"
 #include "log.h"
+#include "filesys.h"
 
 Database_SQLite3::Database_SQLite3(ServerMap *map, std::string savedir)
 {
@@ -63,13 +64,13 @@ int Database_SQLite3::Initialized(void)
 void Database_SQLite3::beginSave() {
        verifyDatabase();
        if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
-               infostream<<"WARNING: beginSave() failed, saving might be slow.";
+               errorstream<<"WARNING: beginSave() failed, saving might be slow.";
 }
 
 void Database_SQLite3::endSave() {
        verifyDatabase();
        if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
-               infostream<<"WARNING: endSave() failed, map might not have saved.";
+               errorstream<<"WARNING: endSave() failed, map might not have saved.";
 }
 
 void Database_SQLite3::createDirs(std::string path)
@@ -99,7 +100,7 @@ void Database_SQLite3::verifyDatabase() {
 
        d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
        if(d != SQLITE_OK) {
-               infostream<<"WARNING: SQLite3 database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
+               errorstream<<"SQLite3 database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
                throw FileNotGoodException("Cannot open database file");
        }
 
@@ -110,199 +111,125 @@ void Database_SQLite3::verifyDatabase() {
                         + itos(g_settings->getU16("sqlite_synchronous"));
        d = sqlite3_exec(m_database, querystr.c_str(), NULL, NULL, NULL);
        if(d != SQLITE_OK) {
-               infostream<<"WARNING: Database pragma set failed: "
+               errorstream<<"Database pragma set failed: "
                                <<sqlite3_errmsg(m_database)<<std::endl;
                throw FileNotGoodException("Cannot set pragma");
        }
 
        d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
        if(d != SQLITE_OK) {
-               infostream<<"WARNING: SQLite3 database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
+               errorstream<<"SQLite3 read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
                throw FileNotGoodException("Cannot prepare read statement");
        }
-
-       d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
+#ifdef __ANDROID__
+       d = sqlite3_prepare(m_database, "INSERT INTO `blocks` VALUES(?, ?);", -1, &m_database_write, NULL);
+#else
+       d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?);", -1, &m_database_write, NULL);
+#endif
        if(d != SQLITE_OK) {
-               infostream<<"WARNING: SQLite3 database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
+               errorstream<<"SQLite3 write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
                throw FileNotGoodException("Cannot prepare write statement");
        }
 
+#ifdef __ANDROID__
+       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");
+       }
+#endif
+
        d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
        if(d != SQLITE_OK) {
-               infostream<<"WARNING: SQLite3 database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
+               infostream<<"SQLite3 list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
                throw FileNotGoodException("Cannot prepare read statement");
        }
 
        infostream<<"ServerMap: SQLite3 database opened"<<std::endl;
 }
 
-void Database_SQLite3::saveBlock(MapBlock *block)
+bool Database_SQLite3::saveBlock(v3s16 blockpos, std::string &data)
 {
-       DSTACK(__FUNCTION_NAME);
-       /*
-               Dummy blocks are not written
-       */
-       if(block->isDummy())
-       {
-               /*v3s16 p = block->getPos();
-               infostream<<"Database_SQLite3::saveBlock(): WARNING: Not writing dummy block "
-                               <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
-               return;
-       }
-
-       // Format used for writing
-       u8 version = SER_FMT_VER_HIGHEST_WRITE;
-       // Get destination
-       v3s16 p3d = block->getPos();
-
+       verifyDatabase();
 
-#if 0
-       v2s16 p2d(p3d.X, p3d.Z);
-       std::string sectordir = getSectorDir(p2d);
+#ifdef __ANDROID__
+       /**
+        * Note: For some unknown reason sqlite3 fails to REPLACE blocks on android,
+        * deleting them and inserting first works.
+        */
+       if (sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK) {
+               infostream << "WARNING: Could not bind block position for load: "
+                       << sqlite3_errmsg(m_database)<<std::endl;
+       }
 
-       createDirs(sectordir);
+       if (sqlite3_step(m_database_read) == SQLITE_ROW) {
+               if (sqlite3_bind_int64(m_database_delete, 1, getBlockAsInteger(blockpos)) != SQLITE_OK) {
+                       infostream << "WARNING: Could not bind block position for delete: "
+                               << sqlite3_errmsg(m_database)<<std::endl;
+               }
 
-       std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
-       std::ofstream o(fullpath.c_str(), std::ios_base::binary);
-       if(o.good() == false)
-               throw FileNotGoodException("Cannot open block data");
+               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);
+       }
+       sqlite3_reset(m_database_read);
 #endif
-       /*
-               [0] u8 serialization version
-               [1] data
-       */
 
-       verifyDatabase();
-
-       std::ostringstream o(std::ios_base::binary);
-
-       o.write((char*)&version, 1);
-
-       // Write basic data
-       block->serialize(o, version, true);
+       if (sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(blockpos)) != 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;
+       }
 
-       // Write block to database
+       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;
+       }
 
-       std::string tmp = o.str();
-       const char *bytes = tmp.c_str();
+       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;
+       }
 
-       if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
-               infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
-       if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
-               infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
-       int written = sqlite3_step(m_database_write);
-       if(written != SQLITE_DONE)
-               infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
-               <<sqlite3_errmsg(m_database)<<std::endl;
-       // Make ready for later reuse
        sqlite3_reset(m_database_write);
 
-       // We just wrote it to the disk so clear modified flag
-       block->resetModified();
+       return true;
 }
 
-MapBlock* Database_SQLite3::loadBlock(v3s16 blockpos)
+std::string Database_SQLite3::loadBlock(v3s16 blockpos)
 {
-       v2s16 p2d(blockpos.X, blockpos.Z);
        verifyDatabase();
 
        if (sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK) {
-               infostream << "WARNING: Could not bind block position for load: "
+               errorstream << "Could not bind block position for load: "
                        << sqlite3_errmsg(m_database)<<std::endl;
        }
 
        if (sqlite3_step(m_database_read) == SQLITE_ROW) {
-               /*
-                       Make sure sector is loaded
-               */
-               MapSector *sector = srvmap->createSector(p2d);
-
-               /*
-                       Load block
-               */
-               const char *data = (const char *)sqlite3_column_blob(m_database_read, 0);
+               const char *data = (const char *) sqlite3_column_blob(m_database_read, 0);
                size_t len = sqlite3_column_bytes(m_database_read, 0);
-               if (data == NULL || len == 0) {
-                       errorstream << "Blank block data in database (data == NULL || len"
-                               " == 0) (" << blockpos.X << "," << blockpos.Y << ","
-                               << blockpos.Z << ")" << std::endl;
-
-                       if (g_settings->getBool("ignore_world_load_errors")) {
-                               errorstream << "Ignoring block load error. Duck and cover! "
-                                       << "(ignore_world_load_errors)" << std::endl;
-                       } else {
-                               throw SerializationError("Blank block data in database");
-                       }
-                       return NULL;
-               }
-
-               std::string datastr(data, len);
-
-               //srvmap->loadBlock(&datastr, blockpos, sector, false);
-
-               try {
-                       std::istringstream is(datastr, std::ios_base::binary);
 
-                       u8 version = SER_FMT_VER_INVALID;
-                       is.read((char *)&version, 1);
-
-                       if (is.fail())
-                               throw SerializationError("ServerMap::loadBlock(): Failed"
-                                        " to read MapBlock version");
-
-                       MapBlock *block = NULL;
-                       bool created_new = false;
-                       block = sector->getBlockNoCreateNoEx(blockpos.Y);
-                       if (block == NULL)
-                       {
-                               block = sector->createBlankBlockNoInsert(blockpos.Y);
-                               created_new = true;
-                       }
-
-                       // Read basic data
-                       block->deSerialize(is, version, true);
-
-                       // If it's a new block, insert it to the map
-                       if (created_new)
-                               sector->insertBlock(block);
-
-                       /*
-                               Save blocks loaded in old format in new format
-                       */
-                       //if(version < SER_FMT_VER_HIGHEST || save_after_load)
-                       // Only save if asked to; no need to update version
-                       //if(save_after_load)
-                       //      saveBlock(block);
-
-                       // We just loaded it from, so it's up-to-date.
-                       block->resetModified();
-               }
-               catch (SerializationError &e)
-               {
-                       errorstream << "Invalid block data in database"
-                               << " (" << blockpos.X << "," << blockpos.Y << "," << blockpos.Z << ")"
-                               << " (SerializationError): " << e.what() << std::endl;
-
-                       // TODO: Block should be marked as invalid in memory so that it is
-                       // not touched but the game can run
-
-                       if (g_settings->getBool("ignore_world_load_errors")) {
-                               errorstream << "Ignoring block load error. Duck and cover! "
-                                       << "(ignore_world_load_errors)" << std::endl;
-                       } else {
-                               throw SerializationError("Invalid block data in database");
-                               //assert(0);
-                       }
-               }
+               std::string s = "";
+               if(data)
+                       s = std::string(data, len);
 
                sqlite3_step(m_database_read);
                // We should never get more than 1 row, so ok to reset
                sqlite3_reset(m_database_read);
 
-               return srvmap->getBlockNoCreateNoEx(blockpos);  // should not be using this here
+               return s;
        }
+
        sqlite3_reset(m_database_read);
-       return NULL;
+       return "";
 }
 
 void Database_SQLite3::createDatabase()
@@ -315,7 +242,7 @@ void Database_SQLite3::createDatabase()
                        "`data` BLOB"
                ");"
        , NULL, NULL, NULL);
-       if(e == SQLITE_ABORT)
+       if(e != SQLITE_OK)
                throw FileNotGoodException("Could not create sqlite3 database structure");
        else
                infostream<<"ServerMap: SQLite3 database structure was created";
@@ -359,4 +286,3 @@ Database_SQLite3::~Database_SQLite3()
                                << "Failed to close database: rc=" << rc << std::endl;
        }
 }
-