about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--hkutil/database.h229
1 files changed, 203 insertions, 26 deletions
diff --git a/hkutil/database.h b/hkutil/database.h index c15fa52..370799d 100644 --- a/hkutil/database.h +++ b/hkutil/database.h
@@ -42,16 +42,24 @@ namespace hatkirby {
42 create 42 create
43 }; 43 };
44 44
45 using blob_type = std::vector<unsigned char>;
46
45 using binding = 47 using binding =
46 mpark::variant< 48 mpark::variant<
47 std::string, 49 std::string,
48 int>; 50 int,
51 double,
52 std::nullptr_t,
53 blob_type>;
49 54
50 using column = 55 using column =
51 std::tuple< 56 std::tuple<
52 std::string, 57 std::string,
53 binding>; 58 binding>;
54 59
60 using row =
61 std::vector<binding>;
62
55 class database { 63 class database {
56 public: 64 public:
57 65
@@ -102,22 +110,21 @@ namespace hatkirby {
102 110
103 void execute(std::string query) 111 void execute(std::string query)
104 { 112 {
105 sqlite3_stmt* ppstmt; 113 sqlite3_stmt* tempStmt;
106 114
107 if (sqlite3_prepare_v2( 115 if (sqlite3_prepare_v2(
108 ppdb_.get(), 116 ppdb_.get(),
109 query.c_str(), 117 query.c_str(),
110 query.length(), 118 query.length(),
111 &ppstmt, 119 &tempStmt,
112 NULL) != SQLITE_OK) 120 NULL) != SQLITE_OK)
113 { 121 {
114 throw sqlite3_error("Error writing to database", ppdb_.get()); 122 throw sqlite3_error("Error writing to database", ppdb_.get());
115 } 123 }
116 124
117 int result = sqlite3_step(ppstmt); 125 ppstmt_type ppstmt(tempStmt);
118 sqlite3_finalize(ppstmt);
119 126
120 if (result != SQLITE_DONE) 127 if (sqlite3_step(ppstmt.get()) != SQLITE_DONE)
121 { 128 {
122 throw sqlite3_error("Error writing to database", ppdb_.get()); 129 throw sqlite3_error("Error writing to database", ppdb_.get());
123 } 130 }
@@ -147,48 +154,150 @@ namespace hatkirby {
147 154
148 std::string query_str = query.str(); 155 std::string query_str = query.str();
149 156
150 sqlite3_stmt* ppstmt; 157 sqlite3_stmt* tempStmt;
151 158
152 if (sqlite3_prepare_v2( 159 if (sqlite3_prepare_v2(
153 ppdb_.get(), 160 ppdb_.get(),
154 query_str.c_str(), 161 query_str.c_str(),
155 query_str.length(), 162 query_str.length(),
156 &ppstmt, 163 &tempStmt,
157 NULL) != SQLITE_OK) 164 NULL) != SQLITE_OK)
158 { 165 {
159 throw sqlite3_error("Error writing to database", ppdb_.get()); 166 throw sqlite3_error("Error writing to database", ppdb_.get());
160 } 167 }
161 168
169 ppstmt_type ppstmt(tempStmt);
170
162 int i = 1; 171 int i = 1;
163 for (const column& c : columns) 172 for (const column& c : columns)
164 { 173 {
165 const binding& b = std::get<1>(c); 174 bindToStmt(ppstmt, i, std::get<1>(c));
166 175
167 if (mpark::holds_alternative<int>(b)) 176 i++;
168 { 177 }
169 sqlite3_bind_int(ppstmt, i, mpark::get<int>(b)); 178
170 } else if (mpark::holds_alternative<std::string>(b)) 179 if (sqlite3_step(ppstmt.get()) != SQLITE_DONE)
180 {
181 throw sqlite3_error("Error writing to database", ppdb_.get());
182 }
183 }
184
185 std::vector<row> queryAll(
186 std::string queryString,
187 std::list<binding> bindings)
188 {
189 sqlite3_stmt* tempStmt;
190
191 if (sqlite3_prepare_v2(
192 ppdb_.get(),
193 queryString.c_str(),
194 queryString.length(),
195 &tempStmt,
196 NULL) != SQLITE_OK)
197 {
198 throw sqlite3_error("Error preparing query", ppdb_.get());
199 }
200
201 ppstmt_type ppstmt(tempStmt);
202
203 int i = 1;
204 for (const binding& value : bindings)
205 {
206 bindToStmt(ppstmt, i, value);
207
208 i++;
209 }
210
211 std::vector<row> result;
212
213 while (sqlite3_step(ppstmt.get()) == SQLITE_ROW)
214 {
215 row curRow;
216
217 int cols = sqlite3_column_count(ppstmt.get());
218
219 for (int i = 0; i < cols; i++)
171 { 220 {
172 const std::string& arg = mpark::get<std::string>(b); 221 switch (sqlite3_column_type(ppstmt.get(), i))
173 222 {
174 sqlite3_bind_text( 223 case SQLITE_INTEGER:
175 ppstmt, 224 {
176 i, 225 curRow.emplace_back(sqlite3_column_int(ppstmt.get(), i));
177 arg.c_str(), 226
178 arg.length(), 227 break;
179 SQLITE_TRANSIENT); 228 }
229
230 case SQLITE_TEXT:
231 {
232 curRow.emplace_back(
233 std::string(
234 reinterpret_cast<const char*>(
235 sqlite3_column_text(ppstmt.get(), i))));
236
237 break;
238 }
239
240 case SQLITE_FLOAT:
241 {
242 curRow.emplace_back(sqlite3_column_double(ppstmt.get(), i));
243
244 break;
245 }
246
247 case SQLITE_NULL:
248 {
249 curRow.emplace_back(nullptr);
250
251 break;
252 }
253
254 case SQLITE_BLOB:
255 {
256 int len = sqlite3_column_bytes(ppstmt.get(), i);
257
258 blob_type value(len);
259
260 memcpy(
261 value.data(),
262 sqlite3_column_blob(ppstmt.get(), i),
263 len);
264
265 curRow.emplace_back(std::move(value));
266
267 break;
268 }
269
270 default:
271 {
272 // Impossible
273
274 break;
275 }
276 }
180 } 277 }
181 278
182 i++; 279 result.emplace_back(std::move(curRow));
183 } 280 }
184 281
185 int result = sqlite3_step(ppstmt); 282 return result;
186 sqlite3_finalize(ppstmt); 283 }
284
285 row queryFirst(
286 std::string queryString,
287 std::list<binding> bindings)
288 {
289 std::vector<row> dataset = queryAll(
290 std::move(queryString),
291 std::move(bindings));
187 292
188 if (result != SQLITE_DONE) 293 if (dataset.empty())
189 { 294 {
190 throw sqlite3_error("Error writing to database", ppdb_.get()); 295 throw std::logic_error("Query returned empty dataset");
191 } 296 }
297
298 row result = std::move(dataset.front());
299
300 return result;
192 } 301 }
193 302
194 private: 303 private:
@@ -202,7 +311,75 @@ namespace hatkirby {
202 } 311 }
203 }; 312 };
204 313
314 class stmt_deleter {
315 public:
316
317 void operator()(sqlite3_stmt* ptr) const
318 {
319 sqlite3_finalize(ptr);
320 }
321 };
322
205 using ppdb_type = std::unique_ptr<sqlite3, sqlite3_deleter>; 323 using ppdb_type = std::unique_ptr<sqlite3, sqlite3_deleter>;
324 using ppstmt_type = std::unique_ptr<sqlite3_stmt, stmt_deleter>;
325
326 void bindToStmt(
327 const ppstmt_type& ppstmt,
328 int i,
329 const binding& value)
330 {
331 if (mpark::holds_alternative<int>(value))
332 {
333 if (sqlite3_bind_int(
334 ppstmt.get(),
335 i,
336 mpark::get<int>(value)) != SQLITE_OK)
337 {
338 throw sqlite3_error("Error preparing statement", ppdb_.get());
339 }
340 } else if (mpark::holds_alternative<std::string>(value))
341 {
342 const std::string& arg = mpark::get<std::string>(value);
343
344 if (sqlite3_bind_text(
345 ppstmt.get(),
346 i,
347 arg.c_str(),
348 arg.length(),
349 SQLITE_TRANSIENT) != SQLITE_OK)
350 {
351 throw sqlite3_error("Error preparing statement", ppdb_.get());
352 }
353 } else if (mpark::holds_alternative<double>(value))
354 {
355 if (sqlite3_bind_double(
356 ppstmt.get(),
357 i,
358 mpark::get<double>(value)) != SQLITE_OK)
359 {
360 throw sqlite3_error("Error preparing statement", ppdb_.get());
361 }
362 } else if (mpark::holds_alternative<std::nullptr_t>(value))
363 {
364 if (sqlite3_bind_null(ppstmt.get(), i) != SQLITE_OK)
365 {
366 throw sqlite3_error("Error preparing statement", ppdb_.get());
367 }
368 } else if (mpark::holds_alternative<blob_type>(value))
369 {
370 const blob_type& arg = mpark::get<blob_type>(value);
371
372 if (sqlite3_bind_blob(
373 ppstmt.get(),
374 i,
375 arg.data(),
376 arg.size(),
377 SQLITE_TRANSIENT) != SQLITE_OK)
378 {
379 throw sqlite3_error("Error preparing statement", ppdb_.get());
380 }
381 }
382 }
206 383
207 ppdb_type ppdb_; 384 ppdb_type ppdb_;
208 385