]> git.ipfire.org Git - pakfire.git/commitdiff
libpakfire: db: Add check function to check database integrity
authorMichael Tremer <michael.tremer@ipfire.org>
Sat, 23 Jan 2021 16:20:49 +0000 (16:20 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Sat, 23 Jan 2021 16:20:49 +0000 (16:20 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/db.c
src/libpakfire/include/pakfire/db.h
src/libpakfire/libpakfire.sym
tests/libpakfire/db.c

index eb70b3f771fe7b7a179f4ba793365d71f3f574bb..ef2427c75ad594ea9bb351b9aaf8fbcfea1dbf5d 100644 (file)
@@ -511,6 +511,119 @@ PAKFIRE_EXPORT struct pakfire_db* pakfire_db_unref(struct pakfire_db* db) {
        return NULL;
 }
 
+static unsigned long pakfire_db_integrity_check(struct pakfire_db* db) {
+       sqlite3_stmt* stmt = NULL;
+       int r;
+
+       r = sqlite3_prepare_v2(db->handle, "PRAGMA integrity_check", -1, &stmt, NULL);
+       if (r) {
+               ERROR(db->pakfire, "Could not prepare integrity check: %s\n",
+                       sqlite3_errmsg(db->handle));
+               return 1;
+       }
+
+       // Count any errors
+       unsigned long errors = 0;
+
+       while (1) {
+               do {
+                       r = sqlite3_step(stmt);
+               } while (r == SQLITE_BUSY);
+
+               if (r == SQLITE_ROW) {
+                       const char* error = (const char*)sqlite3_column_text(stmt, 0);
+
+                       // If the message is "ok", the database has passed the check
+                       if (strcmp(error, "ok") == 0)
+                               continue;
+
+                       // Increment error counter
+                       errors++;
+
+                       // Log the message
+                       ERROR(db->pakfire, "%s\n", error);
+
+               // Break on anything else
+               } else
+                       break;
+       }
+
+       sqlite3_finalize(stmt);
+
+       if (errors)
+               ERROR(db->pakfire, "Database integrity check failed\n");
+       else
+               INFO(db->pakfire, "Database integrity check passed\n");
+
+       return errors;
+}
+
+static unsigned long pakfire_db_foreign_key_check(struct pakfire_db* db) {
+       sqlite3_stmt* stmt = NULL;
+       int r;
+
+       r = sqlite3_prepare_v2(db->handle, "PRAGMA foreign_key_check", -1, &stmt, NULL);
+       if (r) {
+               ERROR(db->pakfire, "Could not prepare foreign key check: %s\n",
+                       sqlite3_errmsg(db->handle));
+               return 1;
+       }
+
+       // Count any errors
+       unsigned long errors = 0;
+
+       while (1) {
+               do {
+                       r = sqlite3_step(stmt);
+               } while (r == SQLITE_BUSY);
+
+               if (r == SQLITE_ROW) {
+                       const unsigned char* table = sqlite3_column_text(stmt, 0);
+                       unsigned long rowid = sqlite3_column_int64(stmt, 1);
+                       const unsigned char* foreign_table = sqlite3_column_text(stmt, 2);
+                       unsigned long foreign_rowid = sqlite3_column_int64(stmt, 3);
+
+                       // Increment error counter
+                       errors++;
+
+                       // Log the message
+                       ERROR(db->pakfire, "Foreign key violation found in %s, row %lu: "
+                               "%lu does not exist in table %s\n", table, rowid, foreign_rowid, foreign_table);
+
+               // Break on anything else
+               } else
+                       break;
+       }
+
+       sqlite3_finalize(stmt);
+
+       if (errors)
+               ERROR(db->pakfire, "Foreign key check failed\n");
+       else
+               INFO(db->pakfire, "Foreign key check passed\n");
+
+       return errors;
+}
+
+/*
+       This function performs an integrity check of the database
+*/
+PAKFIRE_EXPORT int pakfire_db_check(struct pakfire_db* db) {
+       int r;
+
+       // Perform integrity check
+       r = pakfire_db_integrity_check(db);
+       if (r)
+               return 1;
+
+       // Perform foreign key check
+       r = pakfire_db_foreign_key_check(db);
+       if (r)
+               return 1;
+
+       return 0;
+}
+
 PAKFIRE_EXPORT int pakfire_db_add_package(struct pakfire_db* db, PakfirePackage pkg) {
        sqlite3_stmt* stmt = NULL;
        int r;
index bc0f890dfd359e5c2d901308835ab994d0f77893..daad166652f1c785dfdc692334b7fa83ce6e6916 100644 (file)
@@ -35,6 +35,8 @@ int pakfire_db_open(struct pakfire_db** db, Pakfire pakfire, int flags);
 struct pakfire_db* pakfire_db_ref(struct pakfire_db* db);
 struct pakfire_db* pakfire_db_unref(struct pakfire_db* db);
 
+int pakfire_db_check(struct pakfire_db* db);
+
 int pakfire_db_add_package(struct pakfire_db* db, PakfirePackage pkg);
 int pakfire_db_remove_package(struct pakfire_db* db, PakfirePackage pkg);
 
index 27123d7ec4066c4a4be5d8d6a92b9691603e6cbb..67c71f109b4c88d6146dcabb643bad0d08b8e118 100644 (file)
@@ -80,6 +80,7 @@ global:
 
        # db
        pakfire_db_add_package;
+       pakfire_db_check;
        pakfire_db_open;
        pakfire_db_ref;
        pakfire_db_remove_package;
index e0c324e58769f853c41f24ee49b6aff9b9ff17b6..65b98eed1223ee14f78bad0df3c3bbda2fc9e0b9 100644 (file)
@@ -45,9 +45,24 @@ static int test_open_rw(const struct test* t) {
        return 0;
 }
 
+static int test_check(const struct test* t) {
+       struct pakfire_db* db;
+
+       int r = pakfire_db_open(&db, t->pakfire, PAKFIRE_DB_READWRITE);
+       ASSERT(!r);
+
+       // Perform check
+       ASSERT(!pakfire_db_check(db));
+
+       pakfire_db_unref(db);
+
+       return 0;
+}
+
 int main(int argc, char** argv) {
        testsuite_add_test(test_open_ro);
        testsuite_add_test(test_open_rw);
+       testsuite_add_test(test_check);
 
        return testsuite_run();
 }