diff --git a/src/itemdb.d b/src/itemdb.d index 5a2cd616..5bb0e80e 100644 --- a/src/itemdb.d +++ b/src/itemdb.d @@ -2,7 +2,9 @@ import std.datetime; import std.exception; import std.path; import std.string; +import core.stdc.stdlib; import sqlite; +static import log; enum ItemType { file, @@ -29,7 +31,7 @@ struct Item { final class ItemDatabase { // increment this for every change in the db schema - immutable int itemDatabaseVersion = 6; + immutable int itemDatabaseVersion = 7; Database db; Statement insertItemStmt; @@ -41,33 +43,21 @@ final class ItemDatabase this(const(char)[] filename) { db = Database(filename); - if (db.getVersion() == 0) { - db.exec("CREATE TABLE item ( - driveId TEXT NOT NULL, - id TEXT NOT NULL, - name TEXT NOT NULL, - type TEXT NOT NULL, - eTag TEXT, - cTag TEXT, - mtime TEXT NOT NULL, - parentId TEXT, - crc32Hash TEXT, - sha1Hash TEXT, - quickXorHash TEXT, - remoteDriveId TEXT, - remoteId TEXT, - deltaLink TEXT, - PRIMARY KEY (driveId, id), - FOREIGN KEY (driveId, parentId) - REFERENCES item (driveId, id) - ON DELETE CASCADE - ON UPDATE RESTRICT - )"); - db.exec("CREATE INDEX name_idx ON item (name)"); - db.exec("CREATE INDEX remote_idx ON item (remoteDriveId, remoteId)"); - db.setVersion(itemDatabaseVersion); + int dbVersion; + try { + dbVersion = db.getVersion(); + } catch (SqliteException e) { + // An error was generated - what was the error? + log.error("\nAn internal database error occurred: " ~ e.msg ~ "\n"); + exit(-1); + } + + if (dbVersion == 0) { + createTable(); } else if (db.getVersion() != itemDatabaseVersion) { - throw new Exception("The item database is incompatible, please resync manually"); + log.log("The item database is incompatible, re-creating database table structures"); + db.exec("DROP TABLE item"); + createTable(); } db.exec("PRAGMA foreign_keys = ON"); db.exec("PRAGMA recursive_triggers = ON"); @@ -91,6 +81,34 @@ final class ItemDatabase deleteItemByIdStmt = db.prepare("DELETE FROM item WHERE driveId = ? AND id = ?"); } + void createTable() + { + db.exec("CREATE TABLE item ( + driveId TEXT NOT NULL, + id TEXT NOT NULL, + name TEXT NOT NULL, + type TEXT NOT NULL, + eTag TEXT, + cTag TEXT, + mtime TEXT NOT NULL, + parentId TEXT, + crc32Hash TEXT, + sha1Hash TEXT, + quickXorHash TEXT, + remoteDriveId TEXT, + remoteId TEXT, + deltaLink TEXT, + PRIMARY KEY (driveId, id), + FOREIGN KEY (driveId, parentId) + REFERENCES item (driveId, id) + ON DELETE CASCADE + ON UPDATE RESTRICT + )"); + db.exec("CREATE INDEX name_idx ON item (name)"); + db.exec("CREATE INDEX remote_idx ON item (remoteDriveId, remoteId)"); + db.setVersion(itemDatabaseVersion); + } + void insert(const ref Item item) { bindItem(item, insertItemStmt); diff --git a/src/sqlite.d b/src/sqlite.d index 028301ed..b1bb148d 100644 --- a/src/sqlite.d +++ b/src/sqlite.d @@ -2,6 +2,8 @@ module sqlite; import std.stdio; import etc.c.sqlite3; import std.string: fromStringz, toStringz; +import core.stdc.stdlib; +static import log; extern (C) immutable(char)* sqlite3_errstr(int); // missing from the std library @@ -48,9 +50,16 @@ struct Database { // https://www.sqlite.org/c3ref/open.html int rc = sqlite3_open(toStringz(filename), &pDb); - if (rc != SQLITE_OK) { + if (rc == SQLITE_CANTOPEN) { + // Database cannot be opened + log.error("\nThe database cannot be opened. Please check the permissions of ~/.config/onedrive/items.sqlite3\n"); close(); - throw new SqliteException(ifromStringz(sqlite3_errstr(rc))); + exit(-1); + } + if (rc != SQLITE_OK) { + log.error("\nA database access error occurred: " ~ getErrorMessage() ~ "\n"); + close(); + exit(-1); } sqlite3_extended_result_codes(pDb, 1); // always use extended result codes } @@ -60,7 +69,9 @@ struct Database // https://www.sqlite.org/c3ref/exec.html int rc = sqlite3_exec(pDb, toStringz(sql), null, null, null); if (rc != SQLITE_OK) { - throw new SqliteException(ifromStringz(sqlite3_errmsg(pDb))); + log.error("\nA database execution error occurred: "~ getErrorMessage() ~ "\n"); + close(); + exit(-1); } } @@ -79,6 +90,11 @@ struct Database return userVersion; } + string getErrorMessage() + { + return ifromStringz(sqlite3_errmsg(pDb)); + } + void setVersion(int userVersion) { import std.conv: to; @@ -135,7 +151,7 @@ struct Statement int rc = sqlite3_step(pStmt); if (rc == SQLITE_BUSY) { // Database is locked by another onedrive process - writeln("The database is currently locked by another process - cannot sync"); + log.error("The database is currently locked by another process - cannot sync"); return; } if (rc == SQLITE_DONE) { @@ -149,7 +165,9 @@ struct Statement column = fromStringz(sqlite3_column_text(pStmt, i)); } } else { - throw new SqliteException(ifromStringz(sqlite3_errmsg(sqlite3_db_handle(pStmt)))); + string errorMessage = ifromStringz(sqlite3_errmsg(sqlite3_db_handle(pStmt))); + log.error("\nA database statement execution error occurred: "~ errorMessage ~ "\n"); + exit(-1); } } }