diff options
| author | Paul Oliver <contact@pauloliver.dev> | 2026-07-14 17:29:02 +0200 |
|---|---|---|
| committer | Paul Oliver <contact@pauloliver.dev> | 2026-07-30 04:47:22 +0200 |
| commit | f2c34b1d2c18270a0327fb4896fa307fae098770 (patch) | |
| tree | af3b7fb569d6b0a702a5a6e3b3bf8773513d21f1 /core/sql.c | |
Initial
Diffstat (limited to 'core/sql.c')
| -rw-r--r-- | core/sql.c | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/core/sql.c b/core/sql.c new file mode 100644 index 0000000..59fc38e --- /dev/null +++ b/core/sql.c @@ -0,0 +1,105 @@ +// file : core/sql.c +// project : Salis-VM +// author : Paul Oliver <contact@pauloliver.dev> + +// index: +// [section] includes +// [section] macros +// [section] globals +// [section] definitions + +// ---------------------------------------------------------------------------- +// [section] includes +// ---------------------------------------------------------------------------- +#include <assert.h> +#include <sqlite3.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> + +#include "logger.h" +#include "sql.h" + +// ---------------------------------------------------------------------------- +// [section] macros +// ---------------------------------------------------------------------------- +#define DATA_PUSH_BUSY_TIMEOUT 600000 + +// ---------------------------------------------------------------------------- +// [section] globals +// ---------------------------------------------------------------------------- +sqlite3 *g_sim_db; + +// ---------------------------------------------------------------------------- +// [section] definitions +// ---------------------------------------------------------------------------- +void sql_open(void) { + sqlite3_open(DATA_PUSH_PATH, &g_sim_db); + assert(g_sim_db); + + // Install busy handler to retry transactions if DB is locked + sqlite3_busy_timeout(g_sim_db, DATA_PUSH_BUSY_TIMEOUT); + + // Enable Write-Ahead Logging (WAL) + // This seems to help prevent DB locks when displaying live data. + // See: https://sqlite.org/wal.html + sql_exec(NULL, NULL, "pragma journal_mode=wal;"); +} + +void sql_close(void) { + assert(g_sim_db); + sqlite3_close(g_sim_db); +} + +void sql_exec(void (*callback)(sqlite3_stmt *sql_stmt, void *data), void *data, const char *sql_format, ...) { + assert(sql_format); + + va_list args; + va_start(args, sql_format); + int sql_len = vsnprintf(NULL, 0, sql_format, args) + 1; + char *sql_str = malloc(sql_len); + assert(sql_str); + va_end(args); + + va_start(args, sql_format); + vsprintf(sql_str, sql_format, args); + va_end(args); + + int sql_res; + sqlite3_stmt *sql_stmt; + + sql_res = sqlite3_prepare_v2(g_sim_db, sql_str, -1, &sql_stmt, NULL); + assert(sql_res == SQLITE_OK); + free(sql_str); + + while (true) { + sql_res = sqlite3_step(sql_stmt); + + if (sql_res == SQLITE_ROW) { + if (callback) { + callback(sql_stmt, data); + } + + continue; + } + + if (sql_res == SQLITE_DONE) { + break; + } + + log_attention("SQLite database returned error %d with message:", sql_res); + log_attention(sqlite3_errmsg(g_sim_db)); + + // Only handle SQLITE_BUSY error, in which case we retry the query. + // Setting 'journal_mode=wal;' should help prevent busy database errors. + if (sql_res == SQLITE_BUSY) { + log_info("Will retry query"); + continue; + } + + assert(false); + } + + sqlite3_finalize(sql_stmt); +} |
