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 :
|