Implement better handling of database errors & issues

* Implement better handling of database exit scenarios when there is zero disk space left on drive where the items database resides (Issue #77)
* Implement better handling of incorrect database permissions
* Implement better handling of different database versions to automatically re-create tables if version mis-match (RHBZ #1598934)
This commit is contained in:
abraunegg 2018-07-23 17:44:32 +10:00
parent a491620c1a
commit deabc0d212
2 changed files with 66 additions and 31 deletions

View file

@ -2,6 +2,8 @@ import std.datetime;
import std.exception;
import std.path;
import std.string;
import std.stdio;
import core.stdc.stdlib;
import sqlite;
enum ItemType {
@ -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?
writeln("\nAn internal database error occurred: " ~ e.msg ~ "\n");
exit(-1);
}
if (dbVersion == 0) {
createTable(dbVersion);
} else if (db.getVersion() != itemDatabaseVersion) {
throw new Exception("The item database is incompatible, please resync manually");
writeln("The item database is incompatible, re-creating database table structures");
db.exec("DROP TABLE item");
createTable(dbVersion);
}
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(int dbVersion)
{
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);

View file

@ -2,6 +2,7 @@ module sqlite;
import std.stdio;
import etc.c.sqlite3;
import std.string: fromStringz, toStringz;
import core.stdc.stdlib;
extern (C) immutable(char)* sqlite3_errstr(int); // missing from the std library
@ -48,9 +49,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
writeln("\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) {
writeln("\nA database access error occurred: " ~ getErrorMessage() ~ "\n");
close();
exit(-1);
}
sqlite3_extended_result_codes(pDb, 1); // always use extended result codes
}
@ -60,7 +68,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)));
writeln("\nA database execution error occurred: "~ getErrorMessage() ~ "\n");
close();
exit(-1);
}
}
@ -79,6 +89,11 @@ struct Database
return userVersion;
}
string getErrorMessage()
{
return ifromStringz(sqlite3_errmsg(pDb));
}
void setVersion(int userVersion)
{
import std.conv: to;
@ -149,7 +164,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)));
writeln("\nA database statement execution error occurred: "~ errorMessage ~ "\n");
exit(-1);
}
}
}