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