]> git.lizzy.rs Git - minetest.git/blob - src/database-sqlite3.cpp
Replace std::list by std::vector into ServerMap::listAllLoadableBlocks ServerMap...
[minetest.git] / src / database-sqlite3.cpp
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         - Initially only replaces sectors/ and sectors2/
23
24         If map.sqlite does not exist in the save dir
25         or the block was not found in the database
26         the map will try to load from sectors folder.
27         In either case, map.sqlite will be created
28         and all future saves will save there.
29
30         Structure of map.sqlite:
31         Tables:
32                 blocks
33                         (PK) INT pos
34                         BLOB data
35 */
36
37
38 #include "database-sqlite3.h"
39
40 #include "map.h"
41 #include "mapsector.h"
42 #include "mapblock.h"
43 #include "serialization.h"
44 #include "main.h"
45 #include "settings.h"
46 #include "log.h"
47 #include "filesys.h"
48
49 Database_SQLite3::Database_SQLite3(ServerMap *map, std::string savedir)
50 {
51         m_database = NULL;
52         m_database_read = NULL;
53         m_database_write = NULL;
54         m_database_list = NULL;
55         m_database_delete = NULL;
56         m_savedir = savedir;
57         srvmap = map;
58 }
59
60 int Database_SQLite3::Initialized(void)
61 {
62         return m_database ? 1 : 0;
63 }
64
65 void Database_SQLite3::beginSave() {
66         verifyDatabase();
67         if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
68                 errorstream<<"WARNING: beginSave() failed, saving might be slow.";
69 }
70
71 void Database_SQLite3::endSave() {
72         verifyDatabase();
73         if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
74                 errorstream<<"WARNING: endSave() failed, map might not have saved.";
75 }
76
77 void Database_SQLite3::createDirs(std::string path)
78 {
79         if(fs::CreateAllDirs(path) == false)
80         {
81                 infostream<<DTIME<<"Database_SQLite3: Failed to create directory "
82                                 <<"\""<<path<<"\""<<std::endl;
83                 throw BaseException("Database_SQLite3 failed to create directory");
84         }
85 }
86
87 void Database_SQLite3::verifyDatabase() {
88         if(m_database)
89                 return;
90
91         std::string dbp = m_savedir + DIR_DELIM "map.sqlite";
92         bool needs_create = false;
93         int d;
94
95         // Open the database connection
96
97         createDirs(m_savedir); // ?
98
99         if(!fs::PathExists(dbp))
100                 needs_create = true;
101
102         d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
103         if(d != SQLITE_OK) {
104                 errorstream<<"SQLite3 database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
105                 throw FileNotGoodException("Cannot open database file");
106         }
107
108         if(needs_create)
109                 createDatabase();
110
111         std::string querystr = std::string("PRAGMA synchronous = ")
112                          + itos(g_settings->getU16("sqlite_synchronous"));
113         d = sqlite3_exec(m_database, querystr.c_str(), NULL, NULL, NULL);
114         if(d != SQLITE_OK) {
115                 errorstream<<"Database pragma set failed: "
116                                 <<sqlite3_errmsg(m_database)<<std::endl;
117                 throw FileNotGoodException("Cannot set pragma");
118         }
119
120         d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
121         if(d != SQLITE_OK) {
122                 errorstream<<"SQLite3 read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
123                 throw FileNotGoodException("Cannot prepare read statement");
124         }
125 #ifdef __ANDROID__
126         d = sqlite3_prepare(m_database, "INSERT INTO `blocks` VALUES(?, ?);", -1, &m_database_write, NULL);
127 #else
128         d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?);", -1, &m_database_write, NULL);
129 #endif
130         if(d != SQLITE_OK) {
131                 errorstream<<"SQLite3 write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
132                 throw FileNotGoodException("Cannot prepare write statement");
133         }
134
135         d = sqlite3_prepare(m_database, "DELETE FROM `blocks` WHERE `pos`=?;", -1, &m_database_delete, NULL);
136         if(d != SQLITE_OK) {
137                 infostream<<"WARNING: SQLite3 database delete statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
138                 throw FileNotGoodException("Cannot prepare delete statement");
139         }
140
141         d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
142         if(d != SQLITE_OK) {
143                 infostream<<"SQLite3 list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
144                 throw FileNotGoodException("Cannot prepare read statement");
145         }
146
147         infostream<<"ServerMap: SQLite3 database opened"<<std::endl;
148 }
149
150 bool Database_SQLite3::deleteBlock(v3s16 blockpos)
151 {
152         verifyDatabase();
153
154         if (sqlite3_bind_int64(m_database_delete, 1,
155                         getBlockAsInteger(blockpos)) != SQLITE_OK) {
156                 errorstream << "WARNING: Could not bind block position for delete: "
157                         << sqlite3_errmsg(m_database) << std::endl;
158         }
159
160         if (sqlite3_step(m_database_delete) != SQLITE_DONE) {
161                 errorstream << "WARNING: deleteBlock: Block failed to delete "
162                         << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
163                 sqlite3_reset(m_database_delete);
164                 return false;
165         }
166
167         sqlite3_reset(m_database_delete);
168         return true;
169 }
170
171 bool Database_SQLite3::saveBlock(v3s16 blockpos, std::string &data)
172 {
173         verifyDatabase();
174
175         s64 bkey = getBlockAsInteger(blockpos);
176
177 #ifdef __ANDROID__
178         /**
179          * Note: For some unknown reason sqlite3 fails to REPLACE blocks on android,
180          * deleting them and inserting first works.
181          */
182         if (sqlite3_bind_int64(m_database_read, 1, bkey) != SQLITE_OK) {
183                 infostream << "WARNING: Could not bind block position for load: "
184                         << sqlite3_errmsg(m_database)<<std::endl;
185         }
186
187         int step_result = sqlite3_step(m_database_read);
188         sqlite3_reset(m_database_read);
189
190         if (step_result == SQLITE_ROW) {
191                 if (sqlite3_bind_int64(m_database_delete, 1, bkey) != SQLITE_OK) {
192                         infostream << "WARNING: Could not bind block position for delete: "
193                                 << sqlite3_errmsg(m_database)<<std::endl;
194                 }
195
196                 if (sqlite3_step(m_database_delete) != SQLITE_DONE) {
197                         errorstream << "WARNING: saveBlock: Block failed to delete "
198                                 << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
199                         return false;
200                 }
201                 sqlite3_reset(m_database_delete);
202         }
203 #endif
204
205         if (sqlite3_bind_int64(m_database_write, 1, bkey) != SQLITE_OK) {
206                 errorstream << "WARNING: saveBlock: Block position failed to bind: "
207                         << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
208                 sqlite3_reset(m_database_write);
209                 return false;
210         }
211
212         if (sqlite3_bind_blob(m_database_write, 2, (void *)data.c_str(),
213                         data.size(), NULL) != SQLITE_OK) {
214                 errorstream << "WARNING: saveBlock: Block data failed to bind: "
215                         << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
216                 sqlite3_reset(m_database_write);
217                 return false;
218         }
219
220         if (sqlite3_step(m_database_write) != SQLITE_DONE) {
221                 errorstream << "WARNING: saveBlock: Block failed to save "
222                         << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
223                 sqlite3_reset(m_database_write);
224                 return false;
225         }
226
227         sqlite3_reset(m_database_write);
228
229         return true;
230 }
231
232 std::string Database_SQLite3::loadBlock(v3s16 blockpos)
233 {
234         verifyDatabase();
235
236         if (sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK) {
237                 errorstream << "Could not bind block position for load: "
238                         << sqlite3_errmsg(m_database)<<std::endl;
239         }
240
241         if (sqlite3_step(m_database_read) == SQLITE_ROW) {
242                 const char *data = (const char *) sqlite3_column_blob(m_database_read, 0);
243                 size_t len = sqlite3_column_bytes(m_database_read, 0);
244
245                 std::string s = "";
246                 if(data)
247                         s = std::string(data, len);
248
249                 sqlite3_step(m_database_read);
250                 // We should never get more than 1 row, so ok to reset
251                 sqlite3_reset(m_database_read);
252
253                 return s;
254         }
255
256         sqlite3_reset(m_database_read);
257         return "";
258 }
259
260 void Database_SQLite3::createDatabase()
261 {
262         int e;
263         assert(m_database);
264         e = sqlite3_exec(m_database,
265                 "CREATE TABLE IF NOT EXISTS `blocks` ("
266                         "`pos` INT NOT NULL PRIMARY KEY,"
267                         "`data` BLOB"
268                 ");"
269         , NULL, NULL, NULL);
270         if(e != SQLITE_OK)
271                 throw FileNotGoodException("Could not create sqlite3 database structure");
272         else
273                 infostream<<"ServerMap: SQLite3 database structure was created";
274
275 }
276
277 void Database_SQLite3::listAllLoadableBlocks(std::vector<v3s16> &dst)
278 {
279         verifyDatabase();
280
281         while(sqlite3_step(m_database_list) == SQLITE_ROW) {
282                 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
283                 v3s16 p = getIntegerAsBlock(block_i);
284                 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
285                 dst.push_back(p);
286         }
287 }
288
289
290 #define FINALIZE_STATEMENT(statement)                                          \
291         if ( statement )                                                           \
292                 rc = sqlite3_finalize(statement);                                      \
293         if ( rc != SQLITE_OK )                                                     \
294                 errorstream << "Database_SQLite3::~Database_SQLite3():"                \
295                         << "Failed to finalize: " << #statement << ": rc=" << rc << std::endl;
296
297 Database_SQLite3::~Database_SQLite3()
298 {
299         int rc = SQLITE_OK;
300
301         FINALIZE_STATEMENT(m_database_read)
302         FINALIZE_STATEMENT(m_database_write)
303         FINALIZE_STATEMENT(m_database_list)
304         FINALIZE_STATEMENT(m_database_delete)
305
306         if(m_database)
307                 rc = sqlite3_close(m_database);
308
309         if (rc != SQLITE_OK) {
310                 errorstream << "Database_SQLite3::~Database_SQLite3(): "
311                                 << "Failed to close database: rc=" << rc << std::endl;
312         }
313 }