Implement better handling of database errors & database disk space issues (Issue #77) (#80)

* 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-08-02 08:28:52 +10:00 committed by GitHub
parent 91d8b7ab93
commit aa995eb3d3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 68 additions and 32 deletions

View file

@ -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);

View file

@ -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);
}
}
}