SQLite Containers
Loading...
Searching...
No Matches
KeyValueDB.hpp
Go to the documentation of this file.
1#pragma once
2
5
6#include "parts/BaseDB.hpp"
7
8namespace sqlite_containers {
9
21 template<class KeyT, class ValueT>
22 class KeyValueDB final : public BaseDB {
23 public:
24
27
30 explicit KeyValueDB(const Config& config) : BaseDB() {
31 set_config(config);
32 }
33
35 ~KeyValueDB() override final = default;
36
37 // --- Operators ---
38
44 template<template <class...> class ContainerT>
45 KeyValueDB& operator=(const ContainerT<KeyT, ValueT>& container) {
46 // Get the default transaction mode from the configuration
47 auto txn_mode = get_config().default_txn_mode;
48
49 execute_in_transaction([this, &container]() {
50 db_reconcile(container);
51 }, txn_mode); // Use transaction mode from the configuration
52 return *this;
53 }
54
60 template<template <class...> class ContainerT = std::map>
61 ContainerT<KeyT, ValueT> operator()() {
62 ContainerT<KeyT, ValueT> container;
63 // Get the default transaction mode from the configuration
64 auto txn_mode = get_config().default_txn_mode;
65
66 execute_in_transaction([this, &container]() {
67 db_load(container);
68 }, txn_mode); // Use transaction mode from the configuration
69 return container;
70 }
71
72 // --- Existing methods ---
73
78 template<template <class...> class ContainerT>
79 void load(ContainerT<KeyT, ValueT>& container) {
80 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
81 db_load(container);
82 }
83
89 template<template <class...> class ContainerT>
90 void load(
91 ContainerT<KeyT, ValueT>& container,
92 const TransactionMode& mode) {
93 execute_in_transaction([this, &container]() {
94 db_load(container);
95 }, mode);
96 }
97
102 template<template <class...> class ContainerT>
103 ContainerT<KeyT, ValueT> retrieve_all() {
104 ContainerT<KeyT, ValueT> container;
105 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
106 db_load(container);
107 return container;
108 }
109
115 template<template <class...> class ContainerT>
116 ContainerT<KeyT, ValueT> retrieve_all(const TransactionMode& mode) {
117 ContainerT<KeyT, ValueT> container;
118 execute_in_transaction([this, &container]() {
119 db_load(container);
120 }, mode);
121 return container;
122 }
123
128 template<template <class...> class ContainerT>
129 void append(const ContainerT<KeyT, ValueT>& container) {
130 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
131 db_append(container);
132 }
133
139 template<template <class...> class ContainerT>
140 void append(
141 const ContainerT<KeyT, ValueT>& container,
142 const TransactionMode& mode) {
143 execute_in_transaction([this, &container]() {
144 db_append(container);
145 }, mode);
146 }
147
152 template<template <class...> class ContainerT>
153 void reconcile(const ContainerT<KeyT, ValueT>& container) {
154 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
155 db_reconcile(container);
156 }
157
163 template<template <class...> class ContainerT>
165 const ContainerT<KeyT, ValueT>& container,
166 const TransactionMode& mode) {
167 execute_in_transaction([this, &container]() {
168 db_reconcile(container);
169 }, mode);
170 }
171
176 void insert(const KeyT &key, const ValueT &value) {
177 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
178 db_insert(key, value);
179 }
180
184 void insert(const std::pair<KeyT, ValueT> &pair) {
185 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
186 db_insert(pair.first, pair.second);
187 }
188
194 bool find(const KeyT &key, ValueT &value) {
195 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
196 return db_find(key, value);
197 }
198
202 std::size_t count() const {
203 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
204 return db_count();
205 }
206
210 bool empty() const {
211 return (db_count() == 0);
212 }
213
217 void remove(const KeyT &key) {
218 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
219 db_remove(key);
220 }
221
224 void clear() {
225 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
226 db_clear();
227 }
228
229 private:
236
241
245 void db_create_table(const Config &config) override final {
246 const std::string table_name = config.table_name.empty() ? "kv_store" : config.table_name;
247 const std::string temp_table_name = config.table_name.empty() ? "kv_temp_store" : config.table_name + "_temp";
248
249 // Create table if they do not exist
250 const std::string create_table_sql =
251 "CREATE TABLE IF NOT EXISTS " + table_name + " ("
252 "key " + get_sqlite_type<KeyT>() + " PRIMARY KEY NOT NULL,"
253 "value " + get_sqlite_type<ValueT>() + " NOT NULL);";
254 execute(m_sqlite_db, create_table_sql);
255
256 // Create the temporary table for synchronization if it does not exist
257 const std::string create_temp_table_sql =
258 "CREATE TEMPORARY TABLE IF NOT EXISTS " + temp_table_name + " ("
259 "key " + get_sqlite_type<KeyT>() + " PRIMARY KEY NOT NULL,"
260 "value " + get_sqlite_type<ValueT>() + " NOT NULL);";
261 execute(m_sqlite_db, create_temp_table_sql);
262
263 // Initialize prepared statements for operations on the main table
264 m_stmt_load.init(m_sqlite_db, "SELECT key, value FROM " + table_name + ";");
265 m_stmt_replace.init(m_sqlite_db, "REPLACE INTO " + table_name + " (key, value) VALUES (?, ?);");
266 m_stmt_get_value.init(m_sqlite_db, "SELECT value FROM " + table_name + " WHERE key = ?;");
267 m_stmt_count.init(m_sqlite_db, "SELECT COUNT(*) FROM " + table_name + ";");
268 m_stmt_remove.init(m_sqlite_db, "DELETE FROM " + table_name + " WHERE key = ?;");
269 m_stmt_clear_main.init(m_sqlite_db, "DELETE FROM " + table_name);
270
271 // Initialize prepared statements for temporary table operations
272 m_stmt_insert_temp.init(m_sqlite_db, "INSERT OR REPLACE INTO " + temp_table_name + " (key, value) VALUES (?, ?);");
273 m_stmt_purge_main.init(m_sqlite_db, "DELETE FROM " + table_name + " WHERE key NOT IN (SELECT key FROM " + temp_table_name + ");");
274 m_stmt_merge_temp.init(m_sqlite_db, "INSERT OR REPLACE INTO " + table_name + " (key, value) SELECT key, value FROM " + temp_table_name + ";");
275 m_stmt_clear_temp.init(m_sqlite_db, "DELETE FROM " + temp_table_name + ";");
276 }
277
282 template<template <class...> class ContainerT>
283 void db_load(ContainerT<KeyT, ValueT>& container) {
284 int err;
285 try {
286 for (;;) {
287 while ((err = m_stmt_load.step()) == SQLITE_ROW) {
288 KeyT key = m_stmt_load.extract_column<KeyT>(0);
289 ValueT value = m_stmt_load.extract_column<ValueT>(1);
290 container.emplace(std::move(key), std::move(value));
291 }
292 if (err == SQLITE_DONE) {
294 return;
295 }
296 if (err == SQLITE_BUSY) {
297 // Handle busy database, retry reading
300 continue;
301 }
302 // Handle SQLite errors
303 std::string err_msg = "SQLite error: ";
304 err_msg += std::to_string(err);
305 err_msg += ", ";
306 err_msg += sqlite3_errmsg(m_sqlite_db);
307 throw sqlite_exception(err_msg, err);
308 }
309 } catch (...) {
311 std::current_exception(),
312 {&m_stmt_load},
313 "Unknown error occurred while loading data from database.");
314 }
315 }
316
322 bool db_find(const KeyT& key, ValueT& value) {
323 bool is_found = false;
324 int err;
325 try {
326 for (;;) {
327 m_stmt_get_value.bind_value<KeyT>(1, key);
328 while ((err = m_stmt_get_value.step()) == SQLITE_ROW) {
329 value = m_stmt_get_value.extract_column<ValueT>(0);
330 is_found = true;
331 }
332 if (err == SQLITE_DONE) {
335 return is_found;
336 }
337 if (err == SQLITE_BUSY) {
338 // Handle busy database, retry reading
342 continue;
343 }
344 // Handle SQLite errors
345 std::string err_msg = "SQLite error: ";
346 err_msg += std::to_string(err);
347 err_msg += ", ";
348 err_msg += sqlite3_errmsg(m_sqlite_db);
349 throw sqlite_exception(err_msg, err);
350 }
351 } catch (...) {
353 std::current_exception(),
355 "Unknown error occurred while retrieving value for the provided key.");
356 }
357 return false;
358 }
359
363 std::size_t db_count() const {
364 std::size_t count = 0;
365 int err;
366 try {
367 for (;;) {
368 while ((err = m_stmt_count.step()) == SQLITE_ROW) {
369 count = m_stmt_count.extract_column<std::size_t>(0);
370 }
371 if (err == SQLITE_DONE) {
373 break;
374 }
375 if (err == SQLITE_BUSY) {
376 // Handle busy database, retry reading
379 continue;
380 }
381 // Handle SQLite errors
382 std::string err_msg = "SQLite error: ";
383 err_msg += std::to_string(err);
384 err_msg += ", ";
385 err_msg += sqlite3_errmsg(m_sqlite_db);
386 throw sqlite_exception(err_msg, err);
387 }
388 } catch (...) {
390 std::current_exception(),
391 {&m_stmt_count},
392 "Unknown error occurred while counting key-value pairs in the database.");
393 }
394 return count;
395 }
396
401 template<template <class...> class ContainerT>
402 void db_append(const ContainerT<KeyT, ValueT>& container) {
403 try {
404 for (const auto& pair : container) {
405 m_stmt_replace.bind_value<KeyT>(1, pair.first);
406 m_stmt_replace.bind_value<ValueT>(2, pair.second);
410 }
411 } catch (...) {
413 std::current_exception(),
415 "Unknown error occurred while appending key-value pairs to the database.");
416 }
417 }
418
425 template<template <class...> class ContainerT>
426 void db_reconcile(const ContainerT<KeyT, ValueT>& container) {
427 try {
428 // Clear the temporary table
431
432 // Insert all new data from the container into the temporary table
433 for (const auto& pair : container) {
434 m_stmt_insert_temp.bind_value<KeyT>(1, pair.first);
435 m_stmt_insert_temp.bind_value<ValueT>(2, pair.second);
439 }
440
441 // Remove old data from the main table that is not in the temporary table
444
445 // Insert or update records from the temporary table into the main table
448
449 // Clear the temporary table
452
453 } catch (...) {
455 std::current_exception(), {
458 },
459 "Unknown error occurred while reconciling data.");
460 }
461 }
462
467 void db_insert(const KeyT &key, const ValueT &value) {
468 try {
469 m_stmt_replace.bind_value<KeyT>(1, key);
470 m_stmt_replace.bind_value<ValueT>(2, value);
474 } catch (...) {
476 std::current_exception(),
478 "Unknown error occurred while inserting key-value pair into the database.");
479 }
480 }
481
485 void db_remove(const KeyT &key) {
486 try {
487 m_stmt_remove.bind_value<KeyT>(1, key);
491 } catch (...) {
493 std::current_exception(),
494 {&m_stmt_remove},
495 "Unknown error occurred while removing key.");
496 }
497 }
498
501 void db_clear() {
502 try {
505 } catch (...) {
507 std::current_exception(),
509 "Unknown error occurred while clearing the database tables.");
510 }
511 }
512
513 }; // KeyValueDB
514
515}; // 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 execute_in_transaction(Func operation, const TransactionMode &mode)
Executes an operation inside a transaction.
Definition BaseDB.hpp:148
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 key-value pairs in a SQLite database.
SqliteStmt m_stmt_purge_main
Statement for purging stale data from the main table.
void db_load(ContainerT< KeyT, ValueT > &container)
Loads data from the database into the container.
KeyValueDB()
Default constructor.
void db_reconcile(const ContainerT< KeyT, ValueT > &container)
Reconciles the content of the database with the container. Synchronizes the main table with the conte...
std::size_t count() const
Returns the number of elements in the database.
SqliteStmt m_stmt_replace
Statement for replacing key-value pairs in the database.
SqliteStmt m_stmt_clear_main
Statement for clearing the main table.
SqliteStmt m_stmt_load
Statement for loading data from the database.
void db_insert(const KeyT &key, const ValueT &value)
Inserts a key-value pair into the database.
void clear()
Clears all key-value pairs from the database.
std::size_t db_count() const
Returns the number of elements in the database.
void remove(const KeyT &key)
Removes a key-value pair from the database.
bool db_find(const KeyT &key, ValueT &value)
Finds a value by key in the database.
SqliteStmt m_stmt_clear_temp
Statement for clearing the temporary table.
void reconcile(const ContainerT< KeyT, ValueT > &container)
Reconciles the database with the container.
void insert(const std::pair< KeyT, ValueT > &pair)
Inserts a key-value pair into the database.
void insert(const KeyT &key, const ValueT &value)
Inserts a key-value pair into the database.
SqliteStmt m_stmt_remove
Statement for removing key-value pair from the database.
~KeyValueDB() override final=default
Destructor.
void db_remove(const KeyT &key)
Removes a key-value pair from the database.
ContainerT< KeyT, ValueT > retrieve_all()
Retrieves all key-value pairs.
bool empty() const
Checks if the database is empty.
bool find(const KeyT &key, ValueT &value)
Finds a value by key.
KeyValueDB(const Config &config)
Constructor with configuration.
void append(const ContainerT< KeyT, ValueT > &container)
Appends data to the database.
void load(ContainerT< KeyT, ValueT > &container, const TransactionMode &mode)
Loads data with a transaction.
ContainerT< KeyT, ValueT > operator()()
Loads all key-value pairs from the database into a container (e.g., std::map or std::unordered_map).
SqliteStmt m_stmt_insert_temp
Statement for inserting data into the temporary table.
SqliteStmt m_stmt_merge_temp
Statement for merging data from the temporary table into the main table.
void db_clear()
Clears all key-value pairs from the database.
void append(const ContainerT< KeyT, ValueT > &container, const TransactionMode &mode)
Appends data with a transaction.
void db_create_table(const Config &config) override final
Creates the main and temporary tables in the database. This method creates both the main key-value ta...
SqliteStmt m_stmt_get_value
Statement for retrieving value by key from the database.
ContainerT< KeyT, ValueT > retrieve_all(const TransactionMode &mode)
Retrieves all key-value pairs with a transaction.
void reconcile(const ContainerT< KeyT, ValueT > &container, const TransactionMode &mode)
Reconciles the database with the container using a transaction.
void db_append(const ContainerT< KeyT, ValueT > &container)
Appends the content of the container to the database.
void load(ContainerT< KeyT, ValueT > &container)
Loads data from the database into the container.
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