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