]> git.lizzy.rs Git - minetest.git/blob - src/database-sqlite3.cpp
38fb2ca1931b62f7c3b37d4573ac761e97f66ea4
[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                         return NULL;
241                 }
242
243                 std::string datastr(data, len);
244
245                 //srvmap->loadBlock(&datastr, blockpos, sector, false);
246
247                 try {
248                         std::istringstream is(datastr, std::ios_base::binary);
249
250                         u8 version = SER_FMT_VER_INVALID;
251                         is.read((char *)&version, 1);
252
253                         if (is.fail())
254                                 throw SerializationError("ServerMap::loadBlock(): Failed"
255                                          " to read MapBlock version");
256
257                         MapBlock *block = NULL;
258                         bool created_new = false;
259                         block = sector->getBlockNoCreateNoEx(blockpos.Y);
260                         if (block == NULL)
261                         {
262                                 block = sector->createBlankBlockNoInsert(blockpos.Y);
263                                 created_new = true;
264                         }
265
266                         // Read basic data
267                         block->deSerialize(is, version, true);
268
269                         // If it's a new block, insert it to the map
270                         if (created_new)
271                                 sector->insertBlock(block);
272
273                         /*
274                                 Save blocks loaded in old format in new format
275                         */
276                         //if(version < SER_FMT_VER_HIGHEST || save_after_load)
277                         // Only save if asked to; no need to update version
278                         //if(save_after_load)
279                         //      saveBlock(block);
280
281                         // We just loaded it from, so it's up-to-date.
282                         block->resetModified();
283                 }
284                 catch (SerializationError &e)
285                 {
286                         errorstream << "Invalid block data in database"
287                                 << " (" << blockpos.X << "," << blockpos.Y << "," << blockpos.Z << ")"
288                                 << " (SerializationError): " << e.what() << std::endl;
289
290                         // TODO: Block should be marked as invalid in memory so that it is
291                         // not touched but the game can run
292
293                         if (g_settings->getBool("ignore_world_load_errors")) {
294                                 errorstream << "Ignoring block load error. Duck and cover! "
295                                         << "(ignore_world_load_errors)" << std::endl;
296                         } else {
297                                 throw SerializationError("Invalid block data in database");
298                                 //assert(0);
299                         }
300                 }
301
302                 sqlite3_step(m_database_read);
303                 // We should never get more than 1 row, so ok to reset
304                 sqlite3_reset(m_database_read);
305
306                 return srvmap->getBlockNoCreateNoEx(blockpos);  // should not be using this here
307         }
308         sqlite3_reset(m_database_read);
309         return NULL;
310 }
311
312 void Database_SQLite3::createDatabase()
313 {
314         int e;
315         assert(m_database);
316         e = sqlite3_exec(m_database,
317                 "CREATE TABLE IF NOT EXISTS `blocks` ("
318                         "`pos` INT NOT NULL PRIMARY KEY,"
319                         "`data` BLOB"
320                 ");"
321         , NULL, NULL, NULL);
322         if(e == SQLITE_ABORT)
323                 throw FileNotGoodException("Could not create sqlite3 database structure");
324         else
325                 infostream<<"ServerMap: SQLite3 database structure was created";
326         
327 }
328
329 void Database_SQLite3::listAllLoadableBlocks(std::list<v3s16> &dst)
330 {
331         verifyDatabase();
332         
333         while(sqlite3_step(m_database_list) == SQLITE_ROW)
334         {
335                 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
336                 v3s16 p = getIntegerAsBlock(block_i);
337                 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
338                 dst.push_back(p);
339         }
340 }
341
342 Database_SQLite3::~Database_SQLite3()
343 {
344         if(m_database_read)
345                 sqlite3_finalize(m_database_read);
346         if(m_database_write)
347                 sqlite3_finalize(m_database_write);
348         if(m_database)
349                 sqlite3_close(m_database);
350 }
351