SQLite Containers
Loading...
Searching...
No Matches
KeyDB.hpp
Go to the documentation of this file.
1#pragma once
2
5
6#include "parts/BaseDB.hpp"
7
9
16 template<class KeyT>
17 class KeyDB final : public BaseDB {
18 public:
19
21 KeyDB() : BaseDB() {}
22
25 KeyDB(const Config& config) : BaseDB() {
26 set_config(config);
27 }
28
30 ~KeyDB() override final = default;
31
32 // --- Operators ---
33
40 template<template <class...> class ContainerT>
41 KeyDB& operator=(const ContainerT<KeyT>& container) {
42 // Get the default transaction mode from the configuration
43 auto txn_mode = get_config().default_txn_mode;
44
45 execute_in_transaction([this, &container]() {
46 db_reconcile(container);
47 }, txn_mode); // Use transaction mode from the configuration
48 return *this;
49 }
50
56 template<template <class...> class ContainerT = std::set>
57 ContainerT<KeyT> operator()() {
58 ContainerT<KeyT> container;
59 // Get the default transaction mode from the configuration
60 auto txn_mode = get_config().default_txn_mode;
61
62 execute_in_transaction([this, &container]() {
63 db_load(container);
64 }, txn_mode); // Use transaction mode from the configuration
65 return container;
66 }
67
68 // --- Existing methods ---
69
74 template<template <class...> class ContainerT>
75 void load(ContainerT<KeyT>& container) {
76 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
77 db_load(container);
78 }
79
85 template<template <class...> class ContainerT>
86 void load(
87 ContainerT<KeyT>& container,
88 const TransactionMode& mode) {
89 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
90 try {
91 db_begin(mode);
92 db_load(container);
93 db_commit();
94 } catch(const sqlite_exception &e) {
96 throw e;
97 } catch(const std::exception &e) {
99 throw sqlite_exception(e.what());
100 } catch(...) {
101 db_rollback();
102 throw sqlite_exception("Unknown error occurred.");
103 }
104 }
105
110 template<template <class...> class ContainerT>
111 ContainerT<KeyT> retrieve_all() {
112 ContainerT<KeyT> container;
113 std::unique_lock<std::mutex> locker(m_sqlite_mutex);
114 db_load(container);
115 locker.unlock();
116 return container;
117 }
118
124 template<template <class...> class ContainerT>
125 ContainerT<KeyT> retrieve_all(const TransactionMode& mode) {
126 ContainerT<KeyT> container;
127 std::unique_lock<std::mutex> locker(m_sqlite_mutex);
128 try {
129 db_begin(mode);
130 db_load(container);
131 db_commit();
132 locker.unlock();
133 } catch(const sqlite_exception &e) {
134 db_rollback();
135 throw e;
136 } catch(const std::exception &e) {
137 db_rollback();
138 throw sqlite_exception(e.what());
139 } catch(...) {
140 db_rollback();
141 throw sqlite_exception("Unknown error occurred.");
142 }
143
144 return container;
145 }
146
151 template<template <class...> class ContainerT>
152 void append(const ContainerT<KeyT>& container) {
153 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
154 db_append(container);
155 }
156
162 template<template <class...> class ContainerT>
163 void append(const ContainerT<KeyT>& container, const TransactionMode& mode) {
164 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
165 try {
166 db_begin(mode);
167 db_append(container);
168 db_commit();
169 } catch(const sqlite_exception &e) {
170 db_rollback();
171 throw e;
172 } catch(const std::exception &e) {
173 db_rollback();
174 throw sqlite_exception(e.what());
175 } catch(...) {
176 db_rollback();
177 throw sqlite_exception("Unknown error occurred.");
178 }
179 }
180
185 template<template <class...> class ContainerT>
186 void reconcile(const ContainerT<KeyT>& container) {
187 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
188 db_reconcile(container);
189 }
190
196 template<template <class...> class ContainerT>
198 const ContainerT<KeyT>& container,
199 const TransactionMode& mode) {
200 execute_in_transaction([this, &container]() {
201 db_reconcile(container);
202 }, mode);
203 }
204
208 void insert(const KeyT &key) {
209 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
210 db_insert(key);
211 }
212
217 bool find(const KeyT &key) {
218 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
219 return db_find(key);
220 }
221
225 std::size_t count() const {
226 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
227 return db_count();
228 }
229
233 bool empty() const {
234 return (db_count() == 0);
235 }
236
240 void remove(const KeyT &key) {
241 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
242 db_remove(key);
243 }
244
247 void clear() {
248 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
249 db_clear();
250 }
251
252 private:
259
264
267 void db_create_table(const Config &config) override final {
268 const std::string table_name = config.table_name.empty() ? "key_store" : config.table_name;
269 const std::string temp_table_name = config.table_name.empty() ? "key_temp_store" : config.table_name + "_temp";
270
271 // Create table if they do not exist
272 const std::string create_table_sql =
273 "CREATE TABLE IF NOT EXISTS " + table_name + " ("
274 "key " + get_sqlite_type<KeyT>() + " PRIMARY KEY NOT NULL);";
275 execute(m_sqlite_db, create_table_sql);
276
277 // Create the temporary table for synchronization if it does not exist
278 const std::string create_temp_table_sql =
279 "CREATE TEMPORARY TABLE IF NOT EXISTS " + temp_table_name + " ("
280 "key " + get_sqlite_type<KeyT>() + " PRIMARY KEY NOT NULL);";
281 execute(m_sqlite_db, create_temp_table_sql);
282
283 // Initialize prepared statements
284 m_stmt_load.init(m_sqlite_db, "SELECT key FROM " + table_name + ";");
285 m_stmt_replace.init(m_sqlite_db, "REPLACE INTO " + table_name + " (key) VALUES (?);");
286 m_stmt_find.init(m_sqlite_db, "SELECT EXISTS(SELECT 1 FROM " + table_name + " WHERE key = ?);");
287 m_stmt_count.init(m_sqlite_db, "SELECT COUNT(*) FROM " + table_name + ";");
288 m_stmt_remove.init(m_sqlite_db, "DELETE FROM " + table_name + " WHERE key = ?;");
289 m_stmt_clear.init(m_sqlite_db, "DELETE FROM " + table_name);
290
291 // Initialize prepared statements for temporary table operations
292 m_stmt_insert_temp.init(m_sqlite_db, "INSERT OR REPLACE INTO " + temp_table_name + " (key) VALUES (?);");
293 m_stmt_purge_main.init(m_sqlite_db, "DELETE FROM " + table_name + " WHERE key NOT IN (SELECT key FROM " + temp_table_name + ");");
294 m_stmt_merge_temp.init(m_sqlite_db, "INSERT OR REPLACE INTO " + table_name + " (key) SELECT key FROM " + temp_table_name + ";");
295 m_stmt_clear_temp.init(m_sqlite_db, "DELETE FROM " + temp_table_name + ";");
296 }
297
302 template<template <class...> class ContainerT>
303 void db_load(ContainerT<KeyT>& container) {
304 int err;
305 try {
306 for (;;) {
307 while ((err = m_stmt_load.step()) == SQLITE_ROW) {
308 KeyT key = m_stmt_load.extract_column<KeyT>(0);
309 add_value(container, key);
310 }
311 if (err == SQLITE_DONE) {
313 return;
314 }
315 if (err == SQLITE_BUSY) {
316 // Handle busy database, retry reading
319 continue;
320 }
321 // Handle SQLite errors
322 std::string err_msg = "SQLite error: ";
323 err_msg += std::to_string(err);
324 err_msg += ", ";
325 err_msg += sqlite3_errmsg(m_sqlite_db);
326 throw sqlite_exception(err_msg, err);
327 }
328 } catch (...) {
330 std::current_exception(),
331 {&m_stmt_load},
332 "Unknown error occurred while loading data from database.");
333 }
334 }
335
340 bool db_find(const KeyT& key) {
341 bool is_found = false;
342 int err;
343 try {
344 for (;;) {
345 m_stmt_find.bind_value<KeyT>(1, key);
346 while ((err = m_stmt_find.step()) == SQLITE_ROW) {
347 is_found = static_cast<bool>(m_stmt_find.extract_column<int>(0));
348 }
349 if (err == SQLITE_DONE) {
352 break;
353 }
354 if (err == SQLITE_BUSY) {
355 // Handle busy database, retry reading
359 continue;
360 }
361 // Handle SQLite errors
362 std::string err_msg = "SQLite error: ";
363 err_msg += std::to_string(err);
364 err_msg += ", ";
365 err_msg += sqlite3_errmsg(m_sqlite_db);
366 throw sqlite_exception(err_msg, err);
367 }
368 } catch (...) {
370 std::current_exception(),
371 {&m_stmt_find},
372 "Unknown error occurred while retrieving value for the provided key.");
373 }
374 return is_found;
375 }
376
380 std::size_t db_count() const {
381 std::size_t count = 0;
382 int err;
383 try {
384 for (;;) {
385 while ((err = m_stmt_count.step()) == SQLITE_ROW) {
386 count = m_stmt_count.extract_column<std::size_t>(0);
387 }
388 if (err == SQLITE_DONE) {
390 break;
391 }
392 if (err == SQLITE_BUSY) {
393 // Handle busy database, retry reading
396 continue;
397 }
398 // Handle SQLite errors
399 std::string err_msg = "SQLite error: ";
400 err_msg += std::to_string(err);
401 err_msg += ", ";
402 err_msg += sqlite3_errmsg(m_sqlite_db);
403 throw sqlite_exception(err_msg, err);
404 }
405 } catch (...) {
407 std::current_exception(),
408 {&m_stmt_count},
409 "Unknown error occurred while counting keys in the database.");
410 }
411 return count;
412 }
413
420 template<template <class...> class ContainerT>
421 void db_reconcile(const ContainerT<KeyT>& container) {
422 try {
423 // Clear the temporary table
426
427 // Insert all new data from the container into the temporary table
428 for (const auto& item : container) {
429 m_stmt_insert_temp.bind_value<KeyT>(1, item);
433 }
434 // Remove old data from the main table that is not in the temporary table
437
438 // Insert or update records from the temporary table into the main table
441
442 // Clear the temporary table
445
446 } catch (...) {
448 std::current_exception(), {
451 },
452 "Unknown error occurred while reconciling data.");
453 }
454 }
455
460 template<template <class...> class ContainerT>
461 void db_append(const ContainerT<KeyT>& container) {
462 try {
463 for (const auto& item : container) {
464 m_stmt_replace.bind_value<KeyT>(1, item);
468 }
469 } catch (...) {
471 std::current_exception(),
473 "Unknown error occurred while appending key-value pairs to the database.");
474 }
475 }
476
480 void db_insert(const KeyT &key) {
481 try {
482 m_stmt_replace.bind_value<KeyT>(1, key);
486 } catch (...) {
488 std::current_exception(),
490 "Unknown error occurred while inserting key-value pair into the database.");
491 }
492 }
493
497 void db_remove(const KeyT &key) {
498 try {
499 m_stmt_remove.bind_value<KeyT>(1, key);
503 } catch (...) {
505 std::current_exception(),
506 {&m_stmt_remove},
507 "Unknown error occurred while removing key.");
508 }
509 }
510
513 void db_clear() {
514 try {
517 } catch (...) {
519 std::current_exception(),
520 {&m_stmt_clear},
521 "Unknown error occurred while clearing the database tables.");
522 }
523 }
524
525 }; // KeyDB
526
527}; // namespace sqlite_containers
Base class for SQLite database management in sqlite_containers.
#define SQLITE_CONTAINERS_BUSY_RETRY_DELAY_MS
Definition Utils.hpp:22
Base class for SQLite database management.
Definition BaseDB.hpp:23
Config get_config() const
Gets the current configuration of the database.
Definition BaseDB.hpp:45
void db_rollback()
Rolls back the current transaction.
Definition BaseDB.hpp:188
void db_commit()
Commits the current transaction.
Definition BaseDB.hpp:182
void execute_in_transaction(Func operation, const TransactionMode &mode)
Executes an operation inside a transaction.
Definition BaseDB.hpp:148
void db_begin(const TransactionMode &mode=TransactionMode::DEFERRED)
Begins a transaction with the given mode.
Definition BaseDB.hpp:176
void set_config(const Config &config)
Sets the configuration for the database.
Definition BaseDB.hpp:37
void db_handle_exception(std::exception_ptr ex, const std::vector< SqliteStmt * > &stmts, const std::string &message="Unknown error occurred.") const
Handles an exception by resetting and clearing bindings of prepared SQL statements.
Definition BaseDB.hpp:197
Configuration class for SQLite database settings.
Definition Config.hpp:11
TransactionMode default_txn_mode
Definition Config.hpp:29
Template class for managing keys in a SQLite database.
Definition KeyDB.hpp:17
SqliteStmt m_stmt_merge_temp
Statement for merging data from the temporary table into the main table.
Definition KeyDB.hpp:262
ContainerT< KeyT > operator()()
Loads all keys from the database into a container (e.g., std::set, std::unordered_set,...
Definition KeyDB.hpp:57
SqliteStmt m_stmt_purge_main
Statement for purging stale data from the main table.
Definition KeyDB.hpp:261
SqliteStmt m_stmt_clear
Statement for clearing the table.
Definition KeyDB.hpp:258
std::size_t count() const
Returns the number of keys in the database.
Definition KeyDB.hpp:225
bool empty() const
Checks if the database is empty (no keys present).
Definition KeyDB.hpp:233
void db_load(ContainerT< KeyT > &container)
Loads data from the database into the container.
Definition KeyDB.hpp:303
bool find(const KeyT &key)
Finds if a key exists in the database.
Definition KeyDB.hpp:217
void reconcile(const ContainerT< KeyT > &container)
Reconciles the database with the container.
Definition KeyDB.hpp:186
ContainerT< KeyT > retrieve_all()
Retrieves all keys from the database.
Definition KeyDB.hpp:111
SqliteStmt m_stmt_insert_temp
Statement for inserting data into the temporary table.
Definition KeyDB.hpp:260
void db_reconcile(const ContainerT< KeyT > &container)
Reconciles the content of the database with the container. Synchronizes the main table with the conte...
Definition KeyDB.hpp:421
void load(ContainerT< KeyT > &container)
Loads data from the database into the container.
Definition KeyDB.hpp:75
void db_remove(const KeyT &key)
Removes a key from the database.
Definition KeyDB.hpp:497
std::size_t db_count() const
Returns the total number of keys stored in the database.
Definition KeyDB.hpp:380
void append(const ContainerT< KeyT > &container, const TransactionMode &mode)
Appends the content of the container to the database with a transaction.
Definition KeyDB.hpp:163
void db_append(const ContainerT< KeyT > &container)
Appends the content of the container to the database.
Definition KeyDB.hpp:461
void reconcile(const ContainerT< KeyT > &container, const TransactionMode &mode)
Reconciles the database with the container using a transaction.
Definition KeyDB.hpp:197
SqliteStmt m_stmt_load
Statement for loading data from the database.
Definition KeyDB.hpp:253
SqliteStmt m_stmt_clear_temp
Statement for clearing the temporary table.
Definition KeyDB.hpp:263
void db_clear()
Clears all keys from the database.
Definition KeyDB.hpp:513
void db_create_table(const Config &config) override final
Creates the table in the database.
Definition KeyDB.hpp:267
void append(const ContainerT< KeyT > &container)
Appends the content of the container to the database.
Definition KeyDB.hpp:152
KeyDB()
Default constructor.
Definition KeyDB.hpp:21
void insert(const KeyT &key)
Inserts a key into the database.
Definition KeyDB.hpp:208
SqliteStmt m_stmt_remove
Statement for removing a key.
Definition KeyDB.hpp:257
void load(ContainerT< KeyT > &container, const TransactionMode &mode)
Loads data from the database into the container with a transaction.
Definition KeyDB.hpp:86
ContainerT< KeyT > retrieve_all(const TransactionMode &mode)
Retrieves all keys from the database with a transaction.
Definition KeyDB.hpp:125
void db_insert(const KeyT &key)
Inserts a key into the database.
Definition KeyDB.hpp:480
bool db_find(const KeyT &key)
Finds if a key exists in the database.
Definition KeyDB.hpp:340
SqliteStmt m_stmt_count
Statement for counting the number of keys in the database.
Definition KeyDB.hpp:256
SqliteStmt m_stmt_find
Statement for finding a key.
Definition KeyDB.hpp:255
void clear()
Clears all keys from the database.
Definition KeyDB.hpp:247
KeyDB(const Config &config)
Constructor with configuration.
Definition KeyDB.hpp:25
~KeyDB() override final=default
Destructor.
void remove(const KeyT &key)
Removes a key from the database.
Definition KeyDB.hpp:240
SqliteStmt m_stmt_replace
Statement for replacing key-value pairs in the database.
Definition KeyDB.hpp:254
Class for managing SQLite prepared statements.
void clear_bindings()
Clears all bindings on the prepared statement.
void execute(sqlite3 *sqlite_db)
Executes the prepared statement.
T extract_column(const int &index, typename std::enable_if< std::is_integral< T >::value >::type *=0)
Extracts a value from a SQLite statement column.
void reset()
Resets the prepared statement.
void init(sqlite3 *sqlite_db, const char *query)
Initializes the statement.
bool bind_value(const int &index, const T &value, typename std::enable_if< std::is_integral< T >::value >::type *=0)
Binds a value to a SQLite statement.
Exception class for SQLite errors.
Definition Utils.hpp:27
void execute(sqlite3_stmt *stmt)
Executes a SQLite statement.
Definition Utils.hpp:47
std::enable_if< std::is_trivially_copyable< typenameT::value_type >::value &&std::is_same< T, std::vector< typenameT::value_type > >::value, std::string >::type get_sqlite_type()
Definition Utils.hpp:162
TransactionMode
Defines SQLite transaction modes.
Definition Enums.hpp:56
void add_value(ContainerT< T > &container, T &value, typename std::enable_if< std::is_same< ContainerT< T >, std::set< T > >::value||std::is_same< ContainerT< T >, std::unordered_set< T > >::value >::type *=0)
Adds a value to a container (set or unordered_set).
Definition Utils.hpp:184