From: Michael Tremer Date: Thu, 9 Dec 2021 15:57:20 +0000 (+0000) Subject: db: Store a timestamp when the database was modified last X-Git-Tag: 0.9.28~819 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7b4df7659e6f98f6bc662573ea539f4df22d290d;p=pakfire.git db: Store a timestamp when the database was modified last This is useful to detect whether things have changed since the database was opened and whether things have to be re-read from it. Signed-off-by: Michael Tremer --- diff --git a/src/libpakfire/db.c b/src/libpakfire/db.c index 2edd73241..9deaca54e 100644 --- a/src/libpakfire/db.c +++ b/src/libpakfire/db.c @@ -50,6 +50,7 @@ struct pakfire_db { sqlite3* handle; int schema; + time_t last_modified_at; }; static void logging_callback(void* data, int r, const char* msg) { @@ -59,62 +60,6 @@ static void logging_callback(void* data, int r, const char* msg) { sqlite3_errstr(r), msg); } -static int pakfire_db_execute(struct pakfire_db* db, const char* stmt) { - int r; - - DEBUG(db->pakfire, "Executing database query: %s\n", stmt); - - do { - r = sqlite3_exec(db->handle, stmt, NULL, NULL, NULL); - } while (r == SQLITE_BUSY); - - // Log any errors - if (r) { - ERROR(db->pakfire, "Database query failed: %s\n", sqlite3_errmsg(db->handle)); - } - - return r; -} - -static int pakfire_db_begin_transaction(struct pakfire_db* db) { - return pakfire_db_execute(db, "BEGIN TRANSACTION"); -} - -static int pakfire_db_commit(struct pakfire_db* db) { - return pakfire_db_execute(db, "COMMIT"); -} - -static int pakfire_db_rollback(struct pakfire_db* db) { - return pakfire_db_execute(db, "ROLLBACK"); -} - -/* - This function performs any fast optimization and tries to truncate the WAL log file - to keep the database as compact as possible on disk. -*/ -static void pakfire_db_optimize(struct pakfire_db* db) { - pakfire_db_execute(db, "PRAGMA optimize"); - pakfire_db_execute(db, "PRAGMA wal_checkpoint = TRUNCATE"); -} - -static void pakfire_db_free(struct pakfire_db* db) { - if (db->handle) { - // Optimize the database before it is being closed - pakfire_db_optimize(db); - - // Close database handle - int r = sqlite3_close(db->handle); - if (r != SQLITE_OK) { - ERROR(db->pakfire, "Could not close database handle: %s\n", - sqlite3_errmsg(db->handle)); - } - } - - pakfire_unref(db->pakfire); - - free(db); -} - static sqlite3_value* pakfire_db_get(struct pakfire_db* db, const char* key) { sqlite3_stmt* stmt = NULL; sqlite3_value* val = NULL; @@ -167,6 +112,8 @@ static int pakfire_db_set_int(struct pakfire_db* db, const char* key, int val) { sqlite3_stmt* stmt = NULL; int r; + DEBUG(db->pakfire, "Setting %s to '%d'\n", key, val); + const char* sql = "INSERT INTO settings(key, val) VALUES(?, ?) \ ON CONFLICT (key) DO UPDATE SET val = excluded.val"; @@ -207,6 +154,103 @@ ERROR: return r; } +static time_t pakfire_read_modification_time(struct pakfire_db* db) { + time_t t = 0; + + // Fetch the value from the database + sqlite3_value* value = pakfire_db_get(db, "last_modified_at"); + if (value) { + t = sqlite3_value_int64(value); + sqlite3_value_free(value); + } else { + DEBUG(db->pakfire, "Could not find last modification timestamp\n"); + } + + return t; +} + +static int pakfire_update_modification_time(struct pakfire_db* db) { + // Get the current time in UTC + time_t t = time(NULL); + + // Store it in the database + int r = pakfire_db_set_int(db, "last_modified_at", t); + if (r) + return r; + + // Update the last modification timestamp + db->last_modified_at = t; + + return r; +} + +static int pakfire_db_execute(struct pakfire_db* db, const char* stmt) { + int r; + + DEBUG(db->pakfire, "Executing database query: %s\n", stmt); + + do { + r = sqlite3_exec(db->handle, stmt, NULL, NULL, NULL); + } while (r == SQLITE_BUSY); + + // Log any errors + if (r) { + ERROR(db->pakfire, "Database query failed: %s\n", sqlite3_errmsg(db->handle)); + } + + return r; +} + +static int pakfire_db_begin_transaction(struct pakfire_db* db) { + return pakfire_db_execute(db, "BEGIN TRANSACTION"); +} + +static int pakfire_db_commit(struct pakfire_db* db) { + /* + If the database was opened in read-write mode, we will store the + timestamp of the latest modification to compare whether the database + has been changed mid-transaction. + */ + if (db->mode == PAKFIRE_DB_READWRITE) { + int r = pakfire_update_modification_time(db); + if (r) + return r; + } + + return pakfire_db_execute(db, "COMMIT"); +} + +static int pakfire_db_rollback(struct pakfire_db* db) { + return pakfire_db_execute(db, "ROLLBACK"); +} + +/* + This function performs any fast optimization and tries to truncate the WAL log file + to keep the database as compact as possible on disk. +*/ +static void pakfire_db_optimize(struct pakfire_db* db) { + pakfire_db_execute(db, "PRAGMA optimize"); + pakfire_db_execute(db, "PRAGMA wal_checkpoint = TRUNCATE"); +} + +static void pakfire_db_free(struct pakfire_db* db) { + if (db->handle) { + // Optimize the database before it is being closed + pakfire_db_optimize(db); + + // Close database handle + int r = sqlite3_close(db->handle); + if (r != SQLITE_OK) { + ERROR(db->pakfire, "Could not close database handle: %s\n", + sqlite3_errmsg(db->handle)); + } + } + + pakfire_unref(db->pakfire); + + free(db); +} + static int pakfire_db_get_schema(struct pakfire_db* db) { sqlite3_value* value = pakfire_db_get(db, "schema"); if (!value) @@ -419,6 +463,11 @@ static int pakfire_db_setup(struct pakfire_db* db) { return 1; } + // Read modification timestamp + db->last_modified_at = pakfire_read_modification_time(db); + + DEBUG(db->pakfire, "The database was last modified at %ld\n", db->last_modified_at); + // Done when not in read-write mode if (db->mode != PAKFIRE_DB_READWRITE) return 0;