]> git.lizzy.rs Git - minetest.git/blob - src/database-sqlite3.cpp
7faffb01caa65626d9064ee03d14a822593e711b
[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 #include "filesys.h"
48
49 Database_SQLite3::Database_SQLite3(ServerMap *map, std::string savedir)
50 {
51         m_database = NULL;
52         m_database_read = NULL;
53         m_database_write = NULL;
54         m_database_list = NULL;
55         m_savedir = savedir;
56         srvmap = map;
57 }
58
59 int Database_SQLite3::Initialized(void)
60 {
61         return m_database ? 1 : 0;
62 }
63
64 void Database_SQLite3::beginSave() {
65         verifyDatabase();
66         if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
67                 errorstream<<"WARNING: beginSave() failed, saving might be slow.";
68 }
69
70 void Database_SQLite3::endSave() {
71         verifyDatabase();
72         if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
73                 errorstream<<"WARNING: endSave() failed, map might not have saved.";
74 }
75
76 void Database_SQLite3::createDirs(std::string path)
77 {
78         if(fs::CreateAllDirs(path) == false)
79         {
80                 infostream<<DTIME<<"Database_SQLite3: Failed to create directory "
81                                 <<"\""<<path<<"\""<<std::endl;
82                 throw BaseException("Database_SQLite3 failed to create directory");
83         }
84 }
85
86 void Database_SQLite3::verifyDatabase() {
87         if(m_database)
88                 return;
89
90         std::string dbp = m_savedir + DIR_DELIM "map.sqlite";
91         bool needs_create = false;
92         int d;
93
94         // Open the database connection
95
96         createDirs(m_savedir); // ?
97
98         if(!fs::PathExists(dbp))
99                 needs_create = true;
100
101         d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
102         if(d != SQLITE_OK) {
103                 errorstream<<"SQLite3 database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
104                 throw FileNotGoodException("Cannot open database file");
105         }
106
107         if(needs_create)
108                 createDatabase();
109
110         std::string querystr = std::string("PRAGMA synchronous = ")
111                          + itos(g_settings->getU16("sqlite_synchronous"));
112         d = sqlite3_exec(m_database, querystr.c_str(), NULL, NULL, NULL);
113         if(d != SQLITE_OK) {
114                 errorstream<<"Database pragma set failed: "
115                                 <<sqlite3_errmsg(m_database)<<std::endl;
116                 throw FileNotGoodException("Cannot set pragma");
117         }
118
119         d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
120         if(d != SQLITE_OK) {
121                 errorstream<<"SQLite3 read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
122                 throw FileNotGoodException("Cannot prepare read statement");
123         }
124 #ifdef __ANDROID__
125         d = sqlite3_prepare(m_database, "INSERT INTO `blocks` VALUES(?, ?);", -1, &m_database_write, NULL);
126 #else
127         d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?);", -1, &m_database_write, NULL);
128 #endif
129         if(d != SQLITE_OK) {
130                 errorstream<<"SQLite3 write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
131                 throw FileNotGoodException("Cannot prepare write statement");
132         }
133
134         d = sqlite3_prepare(m_database, "DELETE FROM `blocks` WHERE `pos`=?;", -1, &m_database_delete, NULL);
135         if(d != SQLITE_OK) {
136                 infostream<<"WARNING: SQLite3 database delete statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
137                 throw FileNotGoodException("Cannot prepare delete statement");
138         }
139
140         d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
141         if(d != SQLITE_OK) {
142                 infostream<<"SQLite3 list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
143                 throw FileNotGoodException("Cannot prepare read statement");
144         }
145
146         infostream<<"ServerMap: SQLite3 database opened"<<std::endl;
147 }
148
149 bool Database_SQLite3::deleteBlock(v3s16 blockpos)
150 {
151         verifyDatabase();
152
153         if (sqlite3_bind_int64(m_database_delete, 1,
154                         getBlockAsInteger(blockpos)) != SQLITE_OK) {
155                 errorstream << "WARNING: Could not bind block position for delete: "
156                         << sqlite3_errmsg(m_database) << std::endl;
157         }
158
159         if (sqlite3_step(m_database_delete) != SQLITE_DONE) {
160                 errorstream << "WARNING: deleteBlock: Block failed to delete "
161                         << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
162                 sqlite3_reset(m_database_delete);
163                 return false;
164         }
165
166         sqlite3_reset(m_database_delete);
167         return true;
168 }
169
170 bool Database_SQLite3::saveBlock(v3s16 blockpos, std::string &data)
171 {
172         verifyDatabase();
173
174         s64 bkey = getBlockAsInteger(blockpos);
175
176 #ifdef __ANDROID__
177         /**
178          * Note: For some unknown reason sqlite3 fails to REPLACE blocks on android,
179          * deleting them and inserting first works.
180          */
181         if (sqlite3_bind_int64(m_database_read, 1, bkey) != SQLITE_OK) {
182                 infostream << "WARNING: Could not bind block position for load: "
183                         << sqlite3_errmsg(m_database)<<std::endl;
184         }
185
186         int step_result = sqlite3_step(m_database_read);
187         sqlite3_reset(m_database_read);
188
189         if (step_result == SQLITE_ROW) {
190                 if (sqlite3_bind_int64(m_database_delete, 1, bkey) != SQLITE_OK) {
191                         infostream << "WARNING: Could not bind block position for delete: "
192                                 << sqlite3_errmsg(m_database)<<std::endl;
193                 }
194
195                 if (sqlite3_step(m_database_delete) != SQLITE_DONE) {
196                         errorstream << "WARNING: saveBlock: Block failed to delete "
197                                 << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
198                         return false;
199                 }
200                 sqlite3_reset(m_database_delete);
201         }
202 #endif
203
204         if (sqlite3_bind_int64(m_database_write, 1, bkey) != SQLITE_OK) {
205                 errorstream << "WARNING: saveBlock: Block position failed to bind: "
206                         << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
207                 sqlite3_reset(m_database_write);
208                 return false;
209         }
210
211         if (sqlite3_bind_blob(m_database_write, 2, (void *)data.c_str(),
212                         data.size(), NULL) != SQLITE_OK) {
213                 errorstream << "WARNING: saveBlock: Block data failed to bind: "
214                         << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
215                 sqlite3_reset(m_database_write);
216                 return false;
217         }
218
219         if (sqlite3_step(m_database_write) != SQLITE_DONE) {
220                 errorstream << "WARNING: saveBlock: Block failed to save "
221                         << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
222                 sqlite3_reset(m_database_write);
223                 return false;
224         }
225
226         sqlite3_reset(m_database_write);
227
228         return true;
229 }
230
231 std::string Database_SQLite3::loadBlock(v3s16 blockpos)
232 {
233         verifyDatabase();
234
235         if (sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK) {
236                 errorstream << "Could not bind block position for load: "
237                         << sqlite3_errmsg(m_database)<<std::endl;
238         }
239
240         if (sqlite3_step(m_database_read) == SQLITE_ROW) {
241                 const char *data = (const char *) sqlite3_column_blob(m_database_read, 0);
242                 size_t len = sqlite3_column_bytes(m_database_read, 0);
243
244                 std::string s = "";
245                 if(data)
246                         s = std::string(data, len);
247
248                 sqlite3_step(m_database_read);
249                 // We should never get more than 1 row, so ok to reset
250                 sqlite3_reset(m_database_read);
251
252                 return s;
253         }
254
255         sqlite3_reset(m_database_read);
256         return "";
257 }
258
259 void Database_SQLite3::createDatabase()
260 {
261         int e;
262         assert(m_database);
263         e = sqlite3_exec(m_database,
264                 "CREATE TABLE IF NOT EXISTS `blocks` ("
265                         "`pos` INT NOT NULL PRIMARY KEY,"
266                         "`data` BLOB"
267                 ");"
268         , NULL, NULL, NULL);
269         if(e != SQLITE_OK)
270                 throw FileNotGoodException("Could not create sqlite3 database structure");
271         else
272                 infostream<<"ServerMap: SQLite3 database structure was created";
273
274 }
275
276 void Database_SQLite3::listAllLoadableBlocks(std::list<v3s16> &dst)
277 {
278         verifyDatabase();
279
280         while(sqlite3_step(m_database_list) == SQLITE_ROW)
281         {
282                 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
283                 v3s16 p = getIntegerAsBlock(block_i);
284                 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
285                 dst.push_back(p);
286         }
287 }
288
289
290 #define FINALIZE_STATEMENT(statement)                                          \
291         if ( statement )                                                           \
292                 rc = sqlite3_finalize(statement);                                      \
293         if ( rc != SQLITE_OK )                                                     \
294                 errorstream << "Database_SQLite3::~Database_SQLite3():"                \
295                         << "Failed to finalize: " << #statement << ": rc=" << rc << std::endl;
296
297 Database_SQLite3::~Database_SQLite3()
298 {
299         int rc = SQLITE_OK;
300
301         FINALIZE_STATEMENT(m_database_read)
302         FINALIZE_STATEMENT(m_database_write)
303         FINALIZE_STATEMENT(m_database_list)
304         FINALIZE_STATEMENT(m_database_delete)
305
306         if(m_database)
307                 rc = sqlite3_close(m_database);
308
309         if (rc != SQLITE_OK) {
310                 errorstream << "Database_SQLite3::~Database_SQLite3(): "
311                                 << "Failed to close database: rc=" << rc << std::endl;
312         }
313 }