]> git.lizzy.rs Git - minetest.git/blob - src/database-sqlite3.cpp
Split up mapgen.cpp
[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 #ifdef __ANDROID__
135         d = sqlite3_prepare(m_database, "DELETE FROM `blocks` WHERE `pos`=?;", -1, &m_database_delete, NULL);
136         if(d != SQLITE_OK) {
137                 infostream<<"WARNING: SQLite3 database delete statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
138                 throw FileNotGoodException("Cannot prepare delete statement");
139         }
140 #endif
141
142         d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
143         if(d != SQLITE_OK) {
144                 infostream<<"SQLite3 list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
145                 throw FileNotGoodException("Cannot prepare read statement");
146         }
147
148         infostream<<"ServerMap: SQLite3 database opened"<<std::endl;
149 }
150
151 bool Database_SQLite3::saveBlock(v3s16 blockpos, std::string &data)
152 {
153         verifyDatabase();
154
155 #ifdef __ANDROID__
156         /**
157          * Note: For some unknown reason sqlite3 fails to REPLACE blocks on android,
158          * deleting them and inserting first works.
159          */
160         if (sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK) {
161                 infostream << "WARNING: Could not bind block position for load: "
162                         << sqlite3_errmsg(m_database)<<std::endl;
163         }
164
165         if (sqlite3_step(m_database_read) == SQLITE_ROW) {
166                 if (sqlite3_bind_int64(m_database_delete, 1, getBlockAsInteger(blockpos)) != SQLITE_OK) {
167                         infostream << "WARNING: Could not bind block position for delete: "
168                                 << sqlite3_errmsg(m_database)<<std::endl;
169                 }
170
171                 if (sqlite3_step(m_database_delete) != SQLITE_DONE) {
172                         errorstream << "WARNING: saveBlock: Block failed to delete "
173                                 << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
174                         return false;
175                 }
176                 sqlite3_reset(m_database_delete);
177         }
178         sqlite3_reset(m_database_read);
179 #endif
180
181         if (sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(blockpos)) != SQLITE_OK) {
182                 errorstream << "WARNING: saveBlock: Block position failed to bind: "
183                         << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
184                 sqlite3_reset(m_database_write);
185                 return false;
186         }
187
188         if (sqlite3_bind_blob(m_database_write, 2, (void *) data.c_str(), data.size(), NULL) != SQLITE_OK) {
189                 errorstream << "WARNING: saveBlock: Block data failed to bind: "
190                         << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
191                 sqlite3_reset(m_database_write);
192                 return false;
193         }
194
195         if (sqlite3_step(m_database_write) != SQLITE_DONE) {
196                 errorstream << "WARNING: saveBlock: Block failed to save "
197                         << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
198                 sqlite3_reset(m_database_write);
199                 return false;
200         }
201
202         sqlite3_reset(m_database_write);
203
204         return true;
205 }
206
207 std::string Database_SQLite3::loadBlock(v3s16 blockpos)
208 {
209         verifyDatabase();
210
211         if (sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK) {
212                 errorstream << "Could not bind block position for load: "
213                         << sqlite3_errmsg(m_database)<<std::endl;
214         }
215
216         if (sqlite3_step(m_database_read) == SQLITE_ROW) {
217                 const char *data = (const char *) sqlite3_column_blob(m_database_read, 0);
218                 size_t len = sqlite3_column_bytes(m_database_read, 0);
219
220                 std::string s = "";
221                 if(data)
222                         s = std::string(data, len);
223
224                 sqlite3_step(m_database_read);
225                 // We should never get more than 1 row, so ok to reset
226                 sqlite3_reset(m_database_read);
227
228                 return s;
229         }
230
231         sqlite3_reset(m_database_read);
232         return "";
233 }
234
235 void Database_SQLite3::createDatabase()
236 {
237         int e;
238         assert(m_database);
239         e = sqlite3_exec(m_database,
240                 "CREATE TABLE IF NOT EXISTS `blocks` ("
241                         "`pos` INT NOT NULL PRIMARY KEY,"
242                         "`data` BLOB"
243                 ");"
244         , NULL, NULL, NULL);
245         if(e != SQLITE_OK)
246                 throw FileNotGoodException("Could not create sqlite3 database structure");
247         else
248                 infostream<<"ServerMap: SQLite3 database structure was created";
249
250 }
251
252 void Database_SQLite3::listAllLoadableBlocks(std::list<v3s16> &dst)
253 {
254         verifyDatabase();
255
256         while(sqlite3_step(m_database_list) == SQLITE_ROW)
257         {
258                 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
259                 v3s16 p = getIntegerAsBlock(block_i);
260                 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
261                 dst.push_back(p);
262         }
263 }
264
265
266 #define FINALIZE_STATEMENT(statement)                                          \
267         if ( statement )                                                           \
268                 rc = sqlite3_finalize(statement);                                      \
269         if ( rc != SQLITE_OK )                                                     \
270                 errorstream << "Database_SQLite3::~Database_SQLite3():"                \
271                         << "Failed to finalize: " << #statement << ": rc=" << rc << std::endl;
272
273 Database_SQLite3::~Database_SQLite3()
274 {
275         int rc = SQLITE_OK;
276
277         FINALIZE_STATEMENT(m_database_read)
278         FINALIZE_STATEMENT(m_database_write)
279         FINALIZE_STATEMENT(m_database_list)
280
281         if(m_database)
282                 rc = sqlite3_close(m_database);
283
284         if (rc != SQLITE_OK) {
285                 errorstream << "Database_SQLite3::~Database_SQLite3(): "
286                                 << "Failed to close database: rc=" << rc << std::endl;
287         }
288 }