1ae4d7a4bc7cc757b256ec238938dbb1495e0ccb
[oweals/minetest.git] / src / database-sqlite3.cpp
1 /*
2         SQLite format specification:
3         - Initially only replaces sectors/ and sectors2/
4         
5         If map.sqlite does not exist in the save dir
6         or the block was not found in the database
7         the map will try to load from sectors folder.
8         In either case, map.sqlite will be created
9         and all future saves will save there.
10         
11         Structure of map.sqlite:
12         Tables:
13                 blocks
14                         (PK) INT pos
15                         BLOB data
16 */
17
18 #include "map.h"
19 #include "mapsector.h"
20 #include "mapblock.h"
21 #include "main.h"
22 #include "filesys.h"
23 #include "voxel.h"
24 #include "porting.h"
25 #include "mapgen.h"
26 #include "nodemetadata.h"
27 #include "settings.h"
28 #include "log.h"
29 #include "profiler.h"
30 #include "nodedef.h"
31 #include "gamedef.h"
32 #include "util/directiontables.h"
33 #include "rollback_interface.h"
34
35 #include "database-sqlite3.h"
36
37 Database_SQLite3::Database_SQLite3(ServerMap *map, std::string savedir)
38 {
39         m_database = NULL;
40         m_database_read = NULL;
41         m_database_write = NULL;
42         m_database_list = NULL;
43         m_savedir = savedir;
44         srvmap = map;
45 }
46
47 int Database_SQLite3::Initialized(void)
48 {
49         return m_database ? 1 : 0;
50 }
51
52 void Database_SQLite3::beginSave() {
53         verifyDatabase();
54         if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
55                 infostream<<"WARNING: beginSave() failed, saving might be slow.";
56 }
57
58 void Database_SQLite3::endSave() {
59         verifyDatabase();
60         if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
61                 infostream<<"WARNING: endSave() failed, map might not have saved.";
62 }
63
64 void Database_SQLite3::createDirs(std::string path)
65 {
66         if(fs::CreateAllDirs(path) == false)
67         {
68                 infostream<<DTIME<<"Database_SQLite3: Failed to create directory "
69                                 <<"\""<<path<<"\""<<std::endl;
70                 throw BaseException("Database_SQLite3 failed to create directory");
71         }
72 }
73
74 void Database_SQLite3::verifyDatabase() {
75         if(m_database)
76                 return;
77         
78         {
79                 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
80                 bool needs_create = false;
81                 int d;
82                 
83                 /*
84                         Open the database connection
85                 */
86         
87                 createDirs(m_savedir); // ?
88         
89                 if(!fs::PathExists(dbp))
90                         needs_create = true;
91         
92                 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
93                 if(d != SQLITE_OK) {
94                         infostream<<"WARNING: SQLite3 database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
95                         throw FileNotGoodException("Cannot open database file");
96                 }
97                 
98                 if(needs_create)
99                         createDatabase();
100         
101                 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
102                 if(d != SQLITE_OK) {
103                         infostream<<"WARNING: SQLite3 database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
104                         throw FileNotGoodException("Cannot prepare read statement");
105                 }
106                 
107                 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
108                 if(d != SQLITE_OK) {
109                         infostream<<"WARNING: SQLite3 database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
110                         throw FileNotGoodException("Cannot prepare write statement");
111                 }
112                 
113                 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
114                 if(d != SQLITE_OK) {
115                         infostream<<"WARNING: SQLite3 database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
116                         throw FileNotGoodException("Cannot prepare read statement");
117                 }
118                 
119                 infostream<<"ServerMap: SQLite3 database opened"<<std::endl;
120         }
121 }
122
123 void Database_SQLite3::saveBlock(MapBlock *block)
124 {
125         DSTACK(__FUNCTION_NAME);
126         /*
127                 Dummy blocks are not written
128         */
129         if(block->isDummy())
130         {
131                 /*v3s16 p = block->getPos();
132                 infostream<<"Database_SQLite3::saveBlock(): WARNING: Not writing dummy block "
133                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
134                 return;
135         }
136
137         // Format used for writing
138         u8 version = SER_FMT_VER_HIGHEST_WRITE;
139         // Get destination
140         v3s16 p3d = block->getPos();
141         
142         
143 #if 0
144         v2s16 p2d(p3d.X, p3d.Z);
145         std::string sectordir = getSectorDir(p2d);
146
147         createDirs(sectordir);
148
149         std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
150         std::ofstream o(fullpath.c_str(), std::ios_base::binary);
151         if(o.good() == false)
152                 throw FileNotGoodException("Cannot open block data");
153 #endif
154         /*
155                 [0] u8 serialization version
156                 [1] data
157         */
158         
159         verifyDatabase();
160         
161         std::ostringstream o(std::ios_base::binary);
162         
163         o.write((char*)&version, 1);
164         
165         // Write basic data
166         block->serialize(o, version, true);
167         
168         // Write block to database
169         
170         std::string tmp = o.str();
171         const char *bytes = tmp.c_str();
172         
173         if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
174                 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
175         if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
176                 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
177         int written = sqlite3_step(m_database_write);
178         if(written != SQLITE_DONE)
179                 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
180                 <<sqlite3_errmsg(m_database)<<std::endl;
181         // Make ready for later reuse
182         sqlite3_reset(m_database_write);
183         
184         // We just wrote it to the disk so clear modified flag
185         block->resetModified();
186 }
187
188 MapBlock* Database_SQLite3::loadBlock(v3s16 blockpos)
189 {
190         v2s16 p2d(blockpos.X, blockpos.Z);
191         verifyDatabase();
192         
193         if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
194                 infostream<<"WARNING: Could not bind block position for load: "
195                         <<sqlite3_errmsg(m_database)<<std::endl;
196         if(sqlite3_step(m_database_read) == SQLITE_ROW) {
197                 /*
198                         Make sure sector is loaded
199                 */
200                 MapSector *sector = srvmap->createSector(p2d);
201                 
202                 /*
203                         Load block
204                 */
205                 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
206                 size_t len = sqlite3_column_bytes(m_database_read, 0);
207                 
208                 std::string datastr(data, len);
209                 
210 //                srvmap->loadBlock(&datastr, blockpos, sector, false);
211
212                 try {
213                         std::istringstream is(datastr, std::ios_base::binary);
214                      
215                         u8 version = SER_FMT_VER_INVALID;
216                         is.read((char*)&version, 1);
217
218                         if(is.fail())
219                                 throw SerializationError("ServerMap::loadBlock(): Failed"
220                                                      " to read MapBlock version");
221
222                         MapBlock *block = NULL;
223                         bool created_new = false;
224                         block = sector->getBlockNoCreateNoEx(blockpos.Y);
225                         if(block == NULL)
226                         {
227                                 block = sector->createBlankBlockNoInsert(blockpos.Y);
228                                 created_new = true;
229                         }
230                      
231                         // Read basic data
232                         block->deSerialize(is, version, true);
233                      
234                         // If it's a new block, insert it to the map
235                         if(created_new)
236                                 sector->insertBlock(block);
237                      
238                         /*
239                                 Save blocks loaded in old format in new format
240                         */
241
242                         //if(version < SER_FMT_VER_HIGHEST || save_after_load)
243                         // Only save if asked to; no need to update version
244                         //if(save_after_load)
245                         //      saveBlock(block);
246                      
247                         // We just loaded it from, so it's up-to-date.
248                         block->resetModified();
249
250                 }
251                 catch(SerializationError &e)
252                 {
253                         errorstream<<"Invalid block data in database"
254                                      <<" ("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<")"
255                                      <<" (SerializationError): "<<e.what()<<std::endl;
256                      
257                      // TODO: Block should be marked as invalid in memory so that it is
258                      // not touched but the game can run
259
260                         if(g_settings->getBool("ignore_world_load_errors")){
261                              errorstream<<"Ignoring block load error. Duck and cover! "
262                                              <<"(ignore_world_load_errors)"<<std::endl;
263                         } else {
264                              throw SerializationError("Invalid block data in database");
265                              //assert(0);
266                         }
267                 }
268
269
270                 sqlite3_step(m_database_read);
271                 // We should never get more than 1 row, so ok to reset
272                 sqlite3_reset(m_database_read);
273
274                 return srvmap->getBlockNoCreateNoEx(blockpos);  // should not be using this here
275         }
276         sqlite3_reset(m_database_read);
277         return(NULL);
278 }
279
280 void Database_SQLite3::createDatabase()
281 {
282         int e;
283         assert(m_database);
284         e = sqlite3_exec(m_database,
285                 "CREATE TABLE IF NOT EXISTS `blocks` ("
286                         "`pos` INT NOT NULL PRIMARY KEY,"
287                         "`data` BLOB"
288                 ");"
289         , NULL, NULL, NULL);
290         if(e == SQLITE_ABORT)
291                 throw FileNotGoodException("Could not create sqlite3 database structure");
292         else
293                 infostream<<"ServerMap: SQLite3 database structure was created";
294         
295 }
296
297 void Database_SQLite3::listAllLoadableBlocks(std::list<v3s16> &dst)
298 {
299         verifyDatabase();
300         
301         while(sqlite3_step(m_database_list) == SQLITE_ROW)
302         {
303                 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
304                 v3s16 p = getIntegerAsBlock(block_i);
305                 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
306                 dst.push_back(p);
307         }
308 }
309
310 Database_SQLite3::~Database_SQLite3()
311 {
312         if(m_database_read)
313                 sqlite3_finalize(m_database_read);
314         if(m_database_write)
315                 sqlite3_finalize(m_database_write);
316         if(m_database)
317                 sqlite3_close(m_database);
318 }
319