SQLite Containers
Loading...
Searching...
No Matches
BaseDB.hpp
Go to the documentation of this file.
1#pragma once
2
5
6#include "Config.hpp"
7#include "Utils.hpp"
8#include "SqliteStmt.hpp"
9#include <filesystem>
10#include <future>
11#include <mutex>
12#include <atomic>
13
14#if SQLITE_THREADSAFE != 1
15#error "The project must be built for sqlite multithreading! Set the SQLITE_THREADSAFE=1"
16#endif
17
18namespace sqlite_containers {
19 namespace fs = std::filesystem;
20
23 class BaseDB {
24 public:
25
27 BaseDB() = default;
28
31 virtual ~BaseDB() {
32 disconnect();
33 }
34
37 void set_config(const Config& config) {
38 std::lock_guard<std::mutex> locker(m_config_mutex);
39 m_config_new = config;
40 m_config_update = true;
41 }
42
46 std::lock_guard<std::mutex> locker(m_config_mutex);
47 return m_config;
48 }
49
53 void connect() {
54 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
56 throw sqlite_exception("Database connection already exists and no configuration update required.");
57 }
58 if (m_sqlite_db) {
59 if (!m_config_update) return;
61 sqlite3_close_v2(m_sqlite_db);
62 m_sqlite_db = nullptr;
63 }
64
65 std::unique_lock<std::mutex> config_locker(m_config_mutex);
67 m_config_update = false;
68 config_locker.unlock();
69
70 try {
73 on_db_open();
76 } catch(const sqlite_exception &e) {
77 sqlite3_close_v2(m_sqlite_db);
78 m_sqlite_db = nullptr;
79 throw e;
80 } catch(...) {
81 sqlite3_close_v2(m_sqlite_db);
82 m_sqlite_db = nullptr;
83 throw sqlite_exception("An unspecified error occurred in the database operation.");
84 }
85 }
86
90 void connect(const Config& config) {
91 set_config(config);
92 connect();
93 }
94
97 void disconnect() {
98 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
99 if (!m_sqlite_db) return;
100
101 on_db_close();
102 sqlite3_close_v2(m_sqlite_db);
103 m_sqlite_db = nullptr;
104
105 try {
106 if (m_future.valid()) {
107 while (m_future.wait_for(std::chrono::milliseconds(1)) == std::future_status::timeout) {
108 std::this_thread::sleep_for(std::chrono::milliseconds(1));
109 }
110 m_future.get();
111 }
112 } catch(const sqlite_exception &e) {
113 throw e;
114 } catch(const std::exception &e) {
115 throw sqlite_exception(e.what());
116 } catch(...) {
117 throw sqlite_exception("Error occurred during async operation.");
118 }
119 }
120
125 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
126 db_begin(mode);
127 }
128
131 void commit() {
132 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
133 db_commit();
134 }
135
138 void rollback() {
139 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
140 db_rollback();
141 }
142
147 template<typename Func>
148 void execute_in_transaction(Func operation, const TransactionMode& mode) {
149 std::lock_guard<std::mutex> locker(m_sqlite_mutex);
150 try {
151 db_begin(mode);
152 operation();
153 db_commit();
154 } catch(const sqlite_exception &e) {
155 db_rollback();
156 throw e;
157 } catch(const std::exception &e) {
158 db_rollback();
159 throw sqlite_exception(e.what());
160 } catch(...) {
161 db_rollback();
162 throw sqlite_exception("Unknown error occurred.");
163 }
164 }
165
167 virtual void process() {};
168
169 protected:
170 sqlite3* m_sqlite_db = nullptr;
171 mutable std::mutex m_sqlite_mutex;
172
177 m_stmt_begin[static_cast<size_t>(mode)].execute(m_sqlite_db);
178 }
179
185
191
198 std::exception_ptr ex,
199 const std::vector<SqliteStmt*>& stmts,
200 const std::string& message = "Unknown error occurred.") const {
201 try {
202 for (auto* stmt : stmts) {
203 stmt->reset();
204 stmt->clear_bindings();
205 }
206 if (ex) {
207 std::rethrow_exception(ex);
208 }
209 } catch (const sqlite_exception&) {
210 throw;
211 } catch (const std::exception& e) {
212 throw sqlite_exception(e.what());
213 } catch (...) {
214 throw sqlite_exception(message);
215 }
216 }
217
218 private:
219
220 std::array<SqliteStmt, 3> m_stmt_begin;
223
226 mutable std::mutex m_config_mutex;
227 std::atomic<bool> m_config_update = ATOMIC_VAR_INIT(false);
228
229 std::shared_future<void> m_future;
230
235 void db_create_directories(const Config &config) {
236 fs::path file_path(config.db_path);
237 fs::path parent_dir = file_path.parent_path();
238 if (parent_dir.empty()) return;
239 if (!fs::exists(parent_dir)) {
240 if (!fs::create_directories(parent_dir)) {
241 throw sqlite_exception("Failed to create directories for path: " + parent_dir.string());
242 }
243 }
244 }
245
249 void db_open(const Config &config) {
250 int flags = 0;
251 flags |= config.read_only ? SQLITE_OPEN_READONLY : (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
252 flags |= config.use_uri ? SQLITE_OPEN_URI : 0;
253 flags |= config.in_memory ? SQLITE_OPEN_MEMORY : 0;
254 flags |= SQLITE_OPEN_FULLMUTEX;
255 int err = 0;
256 const char* db_name = config.in_memory ? ":memory:" : config.db_path.c_str();
257 if ((err = sqlite3_open_v2(db_name, &m_sqlite_db, flags, nullptr)) != SQLITE_OK) {
258 std::string error_message = "Cannot open database: ";
259 error_message += sqlite3_errmsg(m_sqlite_db);
260 error_message += " (Error code: ";
261 error_message += std::to_string(err);
262 error_message += ")";
263 throw sqlite_exception(error_message);
264 }
265 }
266
270 void db_init(const Config &config) {
271 execute(m_sqlite_db, "PRAGMA busy_timeout = " + std::to_string(config.busy_timeout) + ";");
272 execute(m_sqlite_db, "PRAGMA page_size = " + std::to_string(config.page_size) + ";");
273 execute(m_sqlite_db, "PRAGMA cache_size = " + std::to_string(config.cache_size) + ";");
274 execute(m_sqlite_db, "PRAGMA analysis_limit = " + std::to_string(config.analysis_limit) + ";");
275 execute(m_sqlite_db, "PRAGMA wal_autocheckpoint = " + std::to_string(config.wal_autocheckpoint) + ";");
276 execute(m_sqlite_db, "PRAGMA journal_mode = " + to_string(config.journal_mode) + ";");
277 execute(m_sqlite_db, "PRAGMA synchronous = " + to_string(config.synchronous) + ";");
278 execute(m_sqlite_db, "PRAGMA locking_mode = " + to_string(config.locking_mode) + ";");
279 execute(m_sqlite_db, "PRAGMA auto_vacuum = " + to_string(config.auto_vacuum_mode) + ";");
280
281 for (size_t i = 0; i < m_stmt_begin.size(); ++i) {
282 m_stmt_begin[i].init(m_sqlite_db, "BEGIN " + to_string(static_cast<TransactionMode>(i)) + " TRANSACTION");
283 }
284 m_stmt_commit.init(m_sqlite_db, "COMMIT");
285 m_stmt_rollback.init(m_sqlite_db, "ROLLBACK");
286
287 if (config.user_version > 0) {
288 execute(m_sqlite_db, "PRAGMA user_version = " + std::to_string(config.user_version) + ";");
289 }
290 if (config.use_async) {
291 m_future = std::async(std::launch::async,
292 [this] {
293 process();
294 }).share();
295 }
296 }
297
298 protected:
299
303 virtual void db_create_table(const Config &config) = 0;
304
307 virtual void on_db_open() {}
308
311 virtual void on_db_close() {}
312
313 }; // BaseDB
314
315}; // namespace sqlite_containers
Contains the declaration of Config class for SQLite database configuration.
Declaration of the SqliteStmt class for managing SQLite prepared statements.
Utility functions for working with SQLite in sqlite_containers.
Base class for SQLite database management.
Definition BaseDB.hpp:23
void connect()
Connects to the database using the current configuration. Initializes a connection to the database by...
Definition BaseDB.hpp:53
std::shared_future< void > m_future
Definition BaseDB.hpp:229
void begin(const TransactionMode &mode=TransactionMode::DEFERRED)
Begins a database transaction.
Definition BaseDB.hpp:124
Config get_config() const
Gets the current configuration of the database.
Definition BaseDB.hpp:45
void commit()
Commits the current transaction.
Definition BaseDB.hpp:131
void db_init(const Config &config)
Initializes the database with the given configuration. Sets database parameters such as busy timeout,...
Definition BaseDB.hpp:270
virtual void db_create_table(const Config &config)=0
Creates tables in the database. Must be implemented in derived classes.
std::atomic< bool > m_config_update
Definition BaseDB.hpp:227
virtual void on_db_close()
Called before the database is closed. Can be overridden in derived classes.
Definition BaseDB.hpp:311
virtual void process()
Processes asynchronous database requests (can be overridden).
Definition BaseDB.hpp:167
std::array< SqliteStmt, 3 > m_stmt_begin
Definition BaseDB.hpp:220
void db_rollback()
Rolls back the current transaction.
Definition BaseDB.hpp:188
void db_open(const Config &config)
Opens the database with the specified configuration.
Definition BaseDB.hpp:249
virtual void on_db_open()
Called after the database is opened. Can be overridden in derived classes.
Definition BaseDB.hpp:307
void db_commit()
Commits the current transaction.
Definition BaseDB.hpp:182
void disconnect()
Disconnects from the database.
Definition BaseDB.hpp:97
void connect(const Config &config)
Connects to the database with the given configuration.
Definition BaseDB.hpp:90
BaseDB()=default
Default constructor.
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 rollback()
Rolls back the current transaction.
Definition BaseDB.hpp:138
void set_config(const Config &config)
Sets the configuration for the database.
Definition BaseDB.hpp:37
void db_create_directories(const Config &config)
Creates necessary directories for the database. This method checks if the parent directory of the dat...
Definition BaseDB.hpp:235
virtual ~BaseDB()
Destructor. Disconnects from the database if connected.
Definition BaseDB.hpp:31
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
int wal_autocheckpoint
WAL auto-checkpoint threshold.
Definition Config.hpp:24
std::string db_path
Path to the SQLite database file.
Definition Config.hpp:13
int analysis_limit
Maximum number of rows to analyze.
Definition Config.hpp:23
AutoVacuumMode auto_vacuum_mode
SQLite auto-vacuum mode.
Definition Config.hpp:28
JournalMode journal_mode
SQLite journal mode.
Definition Config.hpp:25
bool read_only
Whether the database is in read-only mode.
Definition Config.hpp:15
int user_version
User-defined version number for the database schema.
Definition Config.hpp:19
SynchronousMode synchronous
SQLite synchronous mode.
Definition Config.hpp:26
bool use_uri
Whether to use URI for opening the database.
Definition Config.hpp:16
bool use_async
Whether to use asynchronous write.
Definition Config.hpp:18
bool in_memory
Whether the database should be in-memory.
Definition Config.hpp:17
LockingMode locking_mode
SQLite locking mode.
Definition Config.hpp:27
int busy_timeout
Timeout in milliseconds for busy handler.
Definition Config.hpp:20
int page_size
SQLite page size.
Definition Config.hpp:21
int cache_size
SQLite cache size (in pages).
Definition Config.hpp:22
Class for managing SQLite prepared statements.
void execute(sqlite3 *sqlite_db)
Executes the prepared statement.
void init(sqlite3 *sqlite_db, const char *query)
Initializes the statement.
Exception class for SQLite errors.
Definition Utils.hpp:27
std::string to_string(const JournalMode &mode)
Converts JournalMode enum to string representation.
Definition Enums.hpp:65
void execute(sqlite3_stmt *stmt)
Executes a SQLite statement.
Definition Utils.hpp:47
TransactionMode
Defines SQLite transaction modes.
Definition Enums.hpp:56
@ DEFERRED
Waits to lock the database until a write operation is requested.