LCOV - code coverage report
Current view: top level - src - database-sqlite3.cpp (source / functions) Hit Total Coverage
Test: report Lines: 0 94 0.0 %
Date: 2015-07-11 18:23:49 Functions: 0 13 0.0 %

          Line data    Source code
       1             : /*
       2             : Minetest
       3             : Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
       4             : 
       5             : This program is free software; you can redistribute it and/or modify
       6             : it under the terms of the GNU Lesser General Public License as published by
       7             : the Free Software Foundation; either version 2.1 of the License, or
       8             : (at your option) any later version.
       9             : 
      10             : This program is distributed in the hope that it will be useful,
      11             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             : GNU Lesser General Public License for more details.
      14             : 
      15             : You should have received a copy of the GNU Lesser General Public License along
      16             : with this program; if not, write to the Free Software Foundation, Inc.,
      17             : 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      18             : */
      19             : 
      20             : /*
      21             : SQLite format specification:
      22             :         blocks:
      23             :                 (PK) INT id
      24             :                 BLOB data
      25             : */
      26             : 
      27             : 
      28             : #include "database-sqlite3.h"
      29             : 
      30             : #include "log.h"
      31             : #include "filesys.h"
      32             : #include "exceptions.h"
      33             : #include "settings.h"
      34             : #include "util/string.h"
      35             : 
      36             : #include <cassert>
      37             : 
      38             : 
      39             : #define SQLRES(s, r) \
      40             :         if ((s) != (r)) { \
      41             :                 throw FileNotGoodException(std::string(\
      42             :                                         "SQLite3 database error (" \
      43             :                                         __FILE__ ":" TOSTRING(__LINE__) \
      44             :                                         "): ") +\
      45             :                                 sqlite3_errmsg(m_database)); \
      46             :         }
      47             : #define SQLOK(s) SQLRES(s, SQLITE_OK)
      48             : 
      49             : #define PREPARE_STATEMENT(name, query) \
      50             :         SQLOK(sqlite3_prepare_v2(m_database, query, -1, &m_stmt_##name, NULL))
      51             : 
      52             : #define FINALIZE_STATEMENT(statement) \
      53             :         if (sqlite3_finalize(statement) != SQLITE_OK) { \
      54             :                 throw FileNotGoodException(std::string( \
      55             :                         "SQLite3: Failed to finalize " #statement ": ") + \
      56             :                          sqlite3_errmsg(m_database)); \
      57             :         }
      58             : 
      59             : 
      60           0 : Database_SQLite3::Database_SQLite3(const std::string &savedir) :
      61             :         m_initialized(false),
      62             :         m_savedir(savedir),
      63             :         m_database(NULL),
      64             :         m_stmt_read(NULL),
      65             :         m_stmt_write(NULL),
      66             :         m_stmt_list(NULL),
      67             :         m_stmt_delete(NULL),
      68             :         m_stmt_begin(NULL),
      69           0 :         m_stmt_end(NULL)
      70             : {
      71           0 : }
      72             : 
      73           0 : void Database_SQLite3::beginSave() {
      74           0 :         verifyDatabase();
      75           0 :         SQLRES(sqlite3_step(m_stmt_begin), SQLITE_DONE);
      76           0 :         sqlite3_reset(m_stmt_begin);
      77           0 : }
      78             : 
      79           0 : void Database_SQLite3::endSave() {
      80           0 :         verifyDatabase();
      81           0 :         SQLRES(sqlite3_step(m_stmt_end), SQLITE_DONE);
      82           0 :         sqlite3_reset(m_stmt_end);
      83           0 : }
      84             : 
      85           0 : void Database_SQLite3::openDatabase()
      86             : {
      87           0 :         if (m_database) return;
      88             : 
      89           0 :         std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
      90             : 
      91             :         // Open the database connection
      92             : 
      93           0 :         if (!fs::CreateAllDirs(m_savedir)) {
      94           0 :                 infostream << "Database_SQLite3: Failed to create directory \""
      95           0 :                         << m_savedir << "\"" << std::endl;
      96             :                 throw FileNotGoodException("Failed to create database "
      97           0 :                                 "save directory");
      98             :         }
      99             : 
     100           0 :         bool needs_create = !fs::PathExists(dbp);
     101             : 
     102           0 :         if (sqlite3_open_v2(dbp.c_str(), &m_database,
     103             :                         SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
     104             :                         NULL) != SQLITE_OK) {
     105           0 :                 errorstream << "SQLite3 database failed to open: "
     106           0 :                         << sqlite3_errmsg(m_database) << std::endl;
     107           0 :                 throw FileNotGoodException("Cannot open database file");
     108             :         }
     109             : 
     110           0 :         if (needs_create) {
     111           0 :                 createDatabase();
     112             :         }
     113             : 
     114             :         std::string query_str = std::string("PRAGMA synchronous = ")
     115           0 :                          + itos(g_settings->getU16("sqlite_synchronous"));
     116           0 :         SQLOK(sqlite3_exec(m_database, query_str.c_str(), NULL, NULL, NULL));
     117             : }
     118             : 
     119           0 : void Database_SQLite3::verifyDatabase()
     120             : {
     121           0 :         if (m_initialized) return;
     122             : 
     123           0 :         openDatabase();
     124             : 
     125           0 :         PREPARE_STATEMENT(begin, "BEGIN");
     126           0 :         PREPARE_STATEMENT(end, "COMMIT");
     127           0 :         PREPARE_STATEMENT(read, "SELECT `data` FROM `blocks` WHERE `pos` = ? LIMIT 1");
     128             : #ifdef __ANDROID__
     129             :         PREPARE_STATEMENT(write,  "INSERT INTO `blocks` (`pos`, `data`) VALUES (?, ?)");
     130             : #else
     131           0 :         PREPARE_STATEMENT(write, "REPLACE INTO `blocks` (`pos`, `data`) VALUES (?, ?)");
     132             : #endif
     133           0 :         PREPARE_STATEMENT(delete, "DELETE FROM `blocks` WHERE `pos` = ?");
     134           0 :         PREPARE_STATEMENT(list, "SELECT `pos` FROM `blocks`");
     135             : 
     136           0 :         m_initialized = true;
     137             : 
     138           0 :         verbosestream << "ServerMap: SQLite3 database opened." << std::endl;
     139             : }
     140             : 
     141           0 : inline void Database_SQLite3::bindPos(sqlite3_stmt *stmt, const v3s16 &pos, int index)
     142             : {
     143           0 :         SQLOK(sqlite3_bind_int64(stmt, index, getBlockAsInteger(pos)));
     144           0 : }
     145             : 
     146           0 : bool Database_SQLite3::deleteBlock(const v3s16 &pos)
     147             : {
     148           0 :         verifyDatabase();
     149             : 
     150           0 :         bindPos(m_stmt_delete, pos);
     151             : 
     152           0 :         bool good = sqlite3_step(m_stmt_delete) == SQLITE_DONE;
     153           0 :         sqlite3_reset(m_stmt_delete);
     154             : 
     155           0 :         if (!good) {
     156           0 :                 errorstream << "WARNING: deleteBlock: Block failed to delete "
     157           0 :                         << PP(pos) << ": " << sqlite3_errmsg(m_database) << std::endl;
     158             :         }
     159           0 :         return good;
     160             : }
     161             : 
     162           0 : bool Database_SQLite3::saveBlock(const v3s16 &pos, const std::string &data)
     163             : {
     164           0 :         verifyDatabase();
     165             : 
     166             : #ifdef __ANDROID__
     167             :         /**
     168             :          * Note: For some unknown reason SQLite3 fails to REPLACE blocks on Android,
     169             :          * deleting them and then inserting works.
     170             :          */
     171             :         bindPos(m_stmt_read, pos);
     172             : 
     173             :         if (sqlite3_step(m_stmt_read) == SQLITE_ROW) {
     174             :                 deleteBlock(pos);
     175             :         }
     176             :         sqlite3_reset(m_stmt_read);
     177             : #endif
     178             : 
     179           0 :         bindPos(m_stmt_write, pos);
     180           0 :         SQLOK(sqlite3_bind_blob(m_stmt_write, 2, data.data(), data.size(), NULL));
     181             : 
     182           0 :         SQLRES(sqlite3_step(m_stmt_write), SQLITE_DONE)
     183           0 :         sqlite3_reset(m_stmt_write);
     184             : 
     185           0 :         return true;
     186             : }
     187             : 
     188           0 : std::string Database_SQLite3::loadBlock(const v3s16 &pos)
     189             : {
     190           0 :         verifyDatabase();
     191             : 
     192           0 :         bindPos(m_stmt_read, pos);
     193             : 
     194           0 :         if (sqlite3_step(m_stmt_read) != SQLITE_ROW) {
     195           0 :                 sqlite3_reset(m_stmt_read);
     196           0 :                 return "";
     197             :         }
     198           0 :         const char *data = (const char *) sqlite3_column_blob(m_stmt_read, 0);
     199           0 :         size_t len = sqlite3_column_bytes(m_stmt_read, 0);
     200             : 
     201           0 :         std::string s;
     202           0 :         if (data)
     203           0 :                 s = std::string(data, len);
     204             : 
     205           0 :         sqlite3_step(m_stmt_read);
     206             :         // We should never get more than 1 row, so ok to reset
     207           0 :         sqlite3_reset(m_stmt_read);
     208             : 
     209           0 :         return s;
     210             : }
     211             : 
     212           0 : void Database_SQLite3::createDatabase()
     213             : {
     214             :         assert(m_database); // Pre-condition
     215           0 :         SQLOK(sqlite3_exec(m_database,
     216             :                 "CREATE TABLE IF NOT EXISTS `blocks` (\n"
     217             :                 "  `pos` INT PRIMARY KEY,\n"
     218             :                 "  `data` BLOB\n"
     219             :                 ");\n",
     220             :                 NULL, NULL, NULL));
     221           0 : }
     222             : 
     223           0 : void Database_SQLite3::listAllLoadableBlocks(std::vector<v3s16> &dst)
     224             : {
     225           0 :         verifyDatabase();
     226             : 
     227           0 :         while (sqlite3_step(m_stmt_list) == SQLITE_ROW) {
     228           0 :                 dst.push_back(getIntegerAsBlock(sqlite3_column_int64(m_stmt_list, 0)));
     229             :         }
     230           0 :         sqlite3_reset(m_stmt_list);
     231           0 : }
     232             : 
     233           0 : Database_SQLite3::~Database_SQLite3()
     234             : {
     235           0 :         FINALIZE_STATEMENT(m_stmt_read)
     236           0 :         FINALIZE_STATEMENT(m_stmt_write)
     237           0 :         FINALIZE_STATEMENT(m_stmt_list)
     238           0 :         FINALIZE_STATEMENT(m_stmt_begin)
     239           0 :         FINALIZE_STATEMENT(m_stmt_end)
     240           0 :         FINALIZE_STATEMENT(m_stmt_delete)
     241             : 
     242           0 :         if (sqlite3_close(m_database) != SQLITE_OK) {
     243           0 :                 errorstream << "Database_SQLite3::~Database_SQLite3(): "
     244           0 :                                 << "Failed to close database: "
     245           0 :                                 << sqlite3_errmsg(m_database) << std::endl;
     246             :         }
     247           0 : }
     248             : 

Generated by: LCOV version 1.11