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