fb76d8eae6d672d909c8e61e1b8c7ee4921a2ee7
[oweals/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
48 Database_SQLite3::Database_SQLite3(ServerMap *map, std::string savedir)
49 {
50         m_database = NULL;
51         m_database_read = NULL;
52         m_database_write = NULL;
53         m_database_list = NULL;
54         m_savedir = savedir;
55         srvmap = map;
56 }
57
58 int Database_SQLite3::Initialized(void)
59 {
60         return m_database ? 1 : 0;
61 }
62
63 void Database_SQLite3::beginSave() {
64         verifyDatabase();
65         if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
66                 infostream<<"WARNING: beginSave() failed, saving might be slow.";
67 }
68
69 void Database_SQLite3::endSave() {
70         verifyDatabase();
71         if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
72                 infostream<<"WARNING: endSave() failed, map might not have saved.";
73 }
74
75 void Database_SQLite3::createDirs(std::string path)
76 {
77         if(fs::CreateAllDirs(path) == false)
78         {
79                 infostream<<DTIME<<"Database_SQLite3: Failed to create directory "
80                                 <<"\""<<path<<"\""<<std::endl;
81                 throw BaseException("Database_SQLite3 failed to create directory");
82         }
83 }
84
85 void Database_SQLite3::verifyDatabase() {
86         if(m_database)
87                 return;
88
89         std::string dbp = m_savedir + DIR_DELIM "map.sqlite";
90         bool needs_create = false;
91         int d;
92
93         // Open the database connection
94
95         createDirs(m_savedir); // ?
96
97         if(!fs::PathExists(dbp))
98                 needs_create = true;
99
100         d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
101         if(d != SQLITE_OK) {
102                 infostream<<"WARNING: SQLite3 database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
103                 throw FileNotGoodException("Cannot open database file");
104         }
105
106         if(needs_create)
107                 createDatabase();
108
109         std::string querystr = std::string("PRAGMA synchronous = ")
110                          + itos(g_settings->getU16("sqlite_synchronous"));
111         d = sqlite3_exec(m_database, querystr.c_str(), NULL, NULL, NULL);
112         if(d != SQLITE_OK) {
113                 infostream<<"WARNING: Database pragma set failed: "
114                                 <<sqlite3_errmsg(m_database)<<std::endl;
115                 throw FileNotGoodException("Cannot set pragma");
116         }
117
118         d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
119         if(d != SQLITE_OK) {
120                 infostream<<"WARNING: SQLite3 database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
121                 throw FileNotGoodException("Cannot prepare read statement");
122         }
123
124         d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
125         if(d != SQLITE_OK) {
126                 infostream<<"WARNING: SQLite3 database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
127                 throw FileNotGoodException("Cannot prepare write statement");
128         }
129
130         d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
131         if(d != SQLITE_OK) {
132                 infostream<<"WARNING: SQLite3 database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
133                 throw FileNotGoodException("Cannot prepare read statement");
134         }
135
136         infostream<<"ServerMap: SQLite3 database opened"<<std::endl;
137 }
138
139 bool Database_SQLite3::saveBlock(MapBlock *block)
140 {
141         DSTACK(__FUNCTION_NAME);
142
143         v3s16 p3d = block->getPos();
144
145         /*
146                 Dummy blocks are not written, but is not a save failure
147         */
148         if(block->isDummy())
149         {
150                 errorstream << "WARNING: saveBlock: Not writing dummy block "
151                                 << PP(p3d) << std::endl;
152                 return true;
153         }
154
155         // Format used for writing
156         u8 version = SER_FMT_VER_HIGHEST_WRITE;
157
158 #if 0
159         v2s16 p2d(p3d.X, p3d.Z);
160         std::string sectordir = getSectorDir(p2d);
161
162         createDirs(sectordir);
163
164         std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
165         std::ofstream o(fullpath.c_str(), std::ios_base::binary);
166         if(o.good() == false)
167                 throw FileNotGoodException("Cannot open block data");
168 #endif
169         /*
170                 [0] u8 serialization version
171                 [1] data
172         */
173
174         verifyDatabase();
175
176         std::ostringstream o(std::ios_base::binary);
177
178         o.write((char *)&version, 1);
179
180         // Write basic data
181         block->serialize(o, version, true);
182
183         // Write block to database
184         std::string tmp = o.str();
185
186         if (sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK) {
187                 errorstream << "WARNING: saveBlock: Block position failed to bind: "
188                         << PP(p3d) << ": " << sqlite3_errmsg(m_database) << std::endl;
189                 sqlite3_reset(m_database_write);
190                 return false;
191         }
192
193         if (sqlite3_bind_blob(m_database_write, 2, (void *)tmp.c_str(), tmp.size(), NULL) != SQLITE_OK) {
194                 errorstream << "WARNING: saveBlock: Block data failed to bind: "
195                         << PP(p3d) << ": " << sqlite3_errmsg(m_database) << std::endl;
196                 sqlite3_reset(m_database_write);
197                 return false;
198         }
199
200         if (sqlite3_step(m_database_write) != SQLITE_DONE) {
201                 errorstream << "WARNING: saveBlock: Block failed to save "
202                         << PP(p3d) << ": " << sqlite3_errmsg(m_database) << std::endl;
203                 sqlite3_reset(m_database_write);
204                 return false;
205         }
206
207         // We just wrote it to the disk so clear modified flag
208         block->resetModified();
209         sqlite3_reset(m_database_write);
210         return true;
211 }
212
213 MapBlock* Database_SQLite3::loadBlock(v3s16 blockpos)
214 {
215         v2s16 p2d(blockpos.X, blockpos.Z);
216         verifyDatabase();
217
218         if (sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK) {
219                 infostream << "WARNING: Could not bind block position for load: "
220                         << sqlite3_errmsg(m_database)<<std::endl;
221         }
222
223         if (sqlite3_step(m_database_read) == SQLITE_ROW) {
224                 /*
225                         Make sure sector is loaded
226                 */
227                 MapSector *sector = srvmap->createSector(p2d);
228
229                 /*
230                         Load block
231                 */
232                 const char *data = (const char *)sqlite3_column_blob(m_database_read, 0);
233                 size_t len = sqlite3_column_bytes(m_database_read, 0);
234                 if (data == NULL || len == 0) {
235                         errorstream << "Blank block data in database (data == NULL || len"
236                                 " == 0) (" << blockpos.X << "," << blockpos.Y << ","
237                                 << blockpos.Z << ")" << std::endl;
238
239                         if (g_settings->getBool("ignore_world_load_errors")) {
240                                 errorstream << "Ignoring block load error. Duck and cover! "
241                                         << "(ignore_world_load_errors)" << std::endl;
242                         } else {
243                                 throw SerializationError("Blank block data in database");
244                         }
245                         return NULL;
246                 }
247
248                 std::string datastr(data, len);
249
250                 //srvmap->loadBlock(&datastr, blockpos, sector, false);
251
252                 try {
253                         std::istringstream is(datastr, std::ios_base::binary);
254
255                         u8 version = SER_FMT_VER_INVALID;
256                         is.read((char *)&version, 1);
257
258                         if (is.fail())
259                                 throw SerializationError("ServerMap::loadBlock(): Failed"
260                                          " to read MapBlock version");
261
262                         MapBlock *block = NULL;
263                         bool created_new = false;
264                         block = sector->getBlockNoCreateNoEx(blockpos.Y);
265                         if (block == NULL)
266                         {
267                                 block = sector->createBlankBlockNoInsert(blockpos.Y);
268                                 created_new = true;
269                         }
270
271                         // Read basic data
272                         block->deSerialize(is, version, true);
273
274                         // If it's a new block, insert it to the map
275                         if (created_new)
276                                 sector->insertBlock(block);
277
278                         /*
279                                 Save blocks loaded in old format in new format
280                         */
281                         //if(version < SER_FMT_VER_HIGHEST || save_after_load)
282                         // Only save if asked to; no need to update version
283                         //if(save_after_load)
284                         //      saveBlock(block);
285
286                         // We just loaded it from, so it's up-to-date.
287                         block->resetModified();
288                 }
289                 catch (SerializationError &e)
290                 {
291                         errorstream << "Invalid block data in database"
292                                 << " (" << blockpos.X << "," << blockpos.Y << "," << blockpos.Z << ")"
293                                 << " (SerializationError): " << e.what() << std::endl;
294
295                         // TODO: Block should be marked as invalid in memory so that it is
296                         // not touched but the game can run
297
298                         if (g_settings->getBool("ignore_world_load_errors")) {
299                                 errorstream << "Ignoring block load error. Duck and cover! "
300                                         << "(ignore_world_load_errors)" << std::endl;
301                         } else {
302                                 throw SerializationError("Invalid block data in database");
303                                 //assert(0);
304                         }
305                 }
306
307                 sqlite3_step(m_database_read);
308                 // We should never get more than 1 row, so ok to reset
309                 sqlite3_reset(m_database_read);
310
311                 return srvmap->getBlockNoCreateNoEx(blockpos);  // should not be using this here
312         }
313         sqlite3_reset(m_database_read);
314         return NULL;
315 }
316
317 void Database_SQLite3::createDatabase()
318 {
319         int e;
320         assert(m_database);
321         e = sqlite3_exec(m_database,
322                 "CREATE TABLE IF NOT EXISTS `blocks` ("
323                         "`pos` INT NOT NULL PRIMARY KEY,"
324                         "`data` BLOB"
325                 ");"
326         , NULL, NULL, NULL);
327         if(e == SQLITE_ABORT)
328                 throw FileNotGoodException("Could not create sqlite3 database structure");
329         else
330                 infostream<<"ServerMap: SQLite3 database structure was created";
331
332 }
333
334 void Database_SQLite3::listAllLoadableBlocks(std::list<v3s16> &dst)
335 {
336         verifyDatabase();
337
338         while(sqlite3_step(m_database_list) == SQLITE_ROW)
339         {
340                 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
341                 v3s16 p = getIntegerAsBlock(block_i);
342                 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
343                 dst.push_back(p);
344         }
345 }
346
347
348 #define FINALIZE_STATEMENT(statement)                                          \
349         if ( statement )                                                           \
350                 rc = sqlite3_finalize(statement);                                      \
351         if ( rc != SQLITE_OK )                                                     \
352                 errorstream << "Database_SQLite3::~Database_SQLite3():"                \
353                         << "Failed to finalize: " << #statement << ": rc=" << rc << std::endl;
354
355 Database_SQLite3::~Database_SQLite3()
356 {
357         int rc = SQLITE_OK;
358
359         FINALIZE_STATEMENT(m_database_read)
360         FINALIZE_STATEMENT(m_database_write)
361         FINALIZE_STATEMENT(m_database_list)
362
363         if(m_database)
364                 rc = sqlite3_close(m_database);
365
366         if (rc != SQLITE_OK) {
367                 errorstream << "Database_SQLite3::~Database_SQLite3(): "
368                                 << "Failed to close database: rc=" << rc << std::endl;
369         }
370 }
371