1 #include <stdexcept>
2 #include <unistd.h> // for usleep
3 #include <iostream>
4 #include <algorithm>
5 #include <time.h>
6 #include "db-sqlite3.h"
7 #include "types.h"
8
9 #define SQLRES(f, good) \
10 result = (sqlite3_##f);\
11 if (result != good) {\
12 throw std::runtime_error(sqlite3_errmsg(db));\
13 }
14 #define SQLOK(f) SQLRES(f, SQLITE_OK)
15
DBSQLite3(const std::string & mapdir)16 DBSQLite3::DBSQLite3(const std::string &mapdir)
17 {
18 int result;
19 std::string db_name = mapdir + "map.sqlite";
20
21 SQLOK(open_v2(db_name.c_str(), &db, SQLITE_OPEN_READONLY |
22 SQLITE_OPEN_PRIVATECACHE, 0))
23
24 SQLOK(prepare_v2(db,
25 "SELECT pos, data FROM blocks WHERE pos BETWEEN ? AND ?",
26 -1, &stmt_get_blocks_z, NULL))
27
28 SQLOK(prepare_v2(db,
29 "SELECT data FROM blocks WHERE pos = ?",
30 -1, &stmt_get_block_exact, NULL))
31
32 SQLOK(prepare_v2(db,
33 "SELECT pos FROM blocks",
34 -1, &stmt_get_block_pos, NULL))
35
36 SQLOK(prepare_v2(db,
37 "SELECT pos FROM blocks WHERE pos BETWEEN ? AND ?",
38 -1, &stmt_get_block_pos_z, NULL))
39 }
40
41
~DBSQLite3()42 DBSQLite3::~DBSQLite3()
43 {
44 sqlite3_finalize(stmt_get_blocks_z);
45 sqlite3_finalize(stmt_get_block_pos);
46 sqlite3_finalize(stmt_get_block_pos_z);
47 sqlite3_finalize(stmt_get_block_exact);
48
49 if (sqlite3_close(db) != SQLITE_OK) {
50 std::cerr << "Error closing SQLite database." << std::endl;
51 };
52 }
53
54
getPosRange(int64_t & min,int64_t & max,int16_t zPos,int16_t zPos2) const55 inline void DBSQLite3::getPosRange(int64_t &min, int64_t &max, int16_t zPos,
56 int16_t zPos2) const
57 {
58 /* The range of block positions is [-2048, 2047], which turns into [0, 4095]
59 * when casted to unsigned. This didn't actually help me understand the
60 * numbers below, but I wanted to write it down.
61 */
62
63 // Magic numbers!
64 min = encodeBlockPos(BlockPos(0, -2048, zPos));
65 max = encodeBlockPos(BlockPos(0, 2048, zPos2)) - 1;
66 }
67
68
getBlockPos(BlockPos min,BlockPos max)69 std::vector<BlockPos> DBSQLite3::getBlockPos(BlockPos min, BlockPos max)
70 {
71 int result;
72 sqlite3_stmt *stmt;
73
74 if(min.z <= -2048 && max.z >= 2048) {
75 stmt = stmt_get_block_pos;
76 } else {
77 stmt = stmt_get_block_pos_z;
78 int64_t minPos, maxPos;
79 if (min.z < -2048)
80 min.z = -2048;
81 if (max.z > 2048)
82 max.z = 2048;
83 getPosRange(minPos, maxPos, min.z, max.z - 1);
84 SQLOK(bind_int64(stmt, 1, minPos))
85 SQLOK(bind_int64(stmt, 2, maxPos))
86 }
87
88 std::vector<BlockPos> positions;
89 while ((result = sqlite3_step(stmt)) != SQLITE_DONE) {
90 if (result == SQLITE_BUSY) { // Wait some time and try again
91 usleep(10000);
92 } else if (result != SQLITE_ROW) {
93 throw std::runtime_error(sqlite3_errmsg(db));
94 }
95
96 int64_t posHash = sqlite3_column_int64(stmt, 0);
97 BlockPos pos = decodeBlockPos(posHash);
98 if(pos.x >= min.x && pos.x < max.x && pos.y >= min.y && pos.y < max.y)
99 positions.emplace_back(pos);
100 }
101 SQLOK(reset(stmt));
102 return positions;
103 }
104
105
loadBlockCache(int16_t zPos)106 void DBSQLite3::loadBlockCache(int16_t zPos)
107 {
108 int result;
109 blockCache.clear();
110
111 int64_t minPos, maxPos;
112 getPosRange(minPos, maxPos, zPos, zPos);
113
114 SQLOK(bind_int64(stmt_get_blocks_z, 1, minPos));
115 SQLOK(bind_int64(stmt_get_blocks_z, 2, maxPos));
116
117 while ((result = sqlite3_step(stmt_get_blocks_z)) != SQLITE_DONE) {
118 if (result == SQLITE_BUSY) { // Wait some time and try again
119 usleep(10000);
120 } else if (result != SQLITE_ROW) {
121 throw std::runtime_error(sqlite3_errmsg(db));
122 }
123
124 int64_t posHash = sqlite3_column_int64(stmt_get_blocks_z, 0);
125 BlockPos pos = decodeBlockPos(posHash);
126 const unsigned char *data = reinterpret_cast<const unsigned char *>(
127 sqlite3_column_blob(stmt_get_blocks_z, 1));
128 size_t size = sqlite3_column_bytes(stmt_get_blocks_z, 1);
129 blockCache[pos.x].emplace_back(pos, ustring(data, size));
130 }
131 SQLOK(reset(stmt_get_blocks_z))
132 }
133
134
getBlocksOnXZ(BlockList & blocks,int16_t x,int16_t z,int16_t min_y,int16_t max_y)135 void DBSQLite3::getBlocksOnXZ(BlockList &blocks, int16_t x, int16_t z,
136 int16_t min_y, int16_t max_y)
137 {
138 /* Cache the blocks on the given Z coordinate between calls, this only
139 * works due to order in which the TileGenerator asks for blocks. */
140 if (z != blockCachedZ) {
141 loadBlockCache(z);
142 blockCachedZ = z;
143 }
144
145 auto it = blockCache.find(x);
146 if (it == blockCache.end())
147 return;
148
149 if (it->second.empty()) {
150 /* We have swapped this list before, this is not supposed to happen
151 * because it's bad for performance. But rather than silently breaking
152 * do the right thing and load the blocks again. */
153 #ifndef NDEBUG
154 std::cout << "Warning: suboptimal access pattern for sqlite3 backend" << std::endl;
155 #endif
156 loadBlockCache(z);
157 }
158 // Swap lists to avoid copying contents
159 blocks.clear();
160 std::swap(blocks, it->second);
161
162 for (auto it = blocks.begin(); it != blocks.end(); ) {
163 if (it->first.y < min_y || it->first.y >= max_y)
164 it = blocks.erase(it);
165 else
166 it++;
167 }
168 }
169
170
getBlocksByPos(BlockList & blocks,const std::vector<BlockPos> & positions)171 void DBSQLite3::getBlocksByPos(BlockList &blocks,
172 const std::vector<BlockPos> &positions)
173 {
174 int result;
175
176 for (auto pos : positions) {
177 int64_t dbPos = encodeBlockPos(pos);
178 SQLOK(bind_int64(stmt_get_block_exact, 1, dbPos));
179
180 while ((result = sqlite3_step(stmt_get_block_exact)) == SQLITE_BUSY) {
181 usleep(10000); // Wait some time and try again
182 }
183 if (result == SQLITE_DONE) {
184 // no data
185 } else if (result != SQLITE_ROW) {
186 throw std::runtime_error(sqlite3_errmsg(db));
187 } else {
188 const unsigned char *data = reinterpret_cast<const unsigned char *>(
189 sqlite3_column_blob(stmt_get_block_exact, 0));
190 size_t size = sqlite3_column_bytes(stmt_get_block_exact, 0);
191 blocks.emplace_back(pos, ustring(data, size));
192 }
193
194 SQLOK(reset(stmt_get_block_exact))
195 }
196 }
197