App: Add option to restore database when a DB error occurs at startup
Test / Run tests (push) Successful in 2m12s
Test / Run tests (push) Successful in 2m12s
This commit is contained in:
@@ -14,10 +14,17 @@ namespace Elwig.Helpers {
|
||||
private static int VersionOffset = 0;
|
||||
|
||||
public static async Task<Version> CheckDb() {
|
||||
if (!File.Exists(App.Config.DatabaseFile))
|
||||
throw new FileNotFoundException($"Die Datei \"{App.Config.DatabaseFile}\" exisitiert nicht");
|
||||
|
||||
long? applId, schemaVers;
|
||||
using (var cnx = await AppDbContext.ConnectAsync()) {
|
||||
|
||||
await cnx.QuickCheck();
|
||||
|
||||
applId = (long?)await cnx.ExecuteScalar("PRAGMA application_id") ?? 0;
|
||||
if (applId != 0x454C5747) throw new Exception($"Invalid application_id in database (0x{applId:X08})");
|
||||
if (applId != 0x454C5747)
|
||||
throw new FileFormatException($"Invalid application_id in database (0x{applId:X08})");
|
||||
|
||||
schemaVers = (long?)await cnx.ExecuteScalar("PRAGMA schema_version") ?? 0;
|
||||
VersionOffset = (int)(schemaVers % 100);
|
||||
@@ -76,16 +83,16 @@ namespace Elwig.Helpers {
|
||||
try {
|
||||
using var cnx = await AppDbContext.ConnectAsync();
|
||||
await cnx.ExecuteBatch("PRAGMA locking_mode = EXCLUSIVE");
|
||||
|
||||
await cnx.IntegrityCheck();
|
||||
await cnx.ForeignKeyCheck();
|
||||
|
||||
foreach (var script in toExecute) {
|
||||
await cnx.ExecuteEmbeddedScript(asm, script);
|
||||
}
|
||||
|
||||
var violations = await cnx.ForeignKeyCheck();
|
||||
if (violations.Length > 0) {
|
||||
throw new Exception($"Foreign key violations ({violations.Length}):\n" + string.Join("\n", violations
|
||||
.Take(50)
|
||||
.Select(v => $"{v.Table} - {v.RowId} - {v.Parent} - {v.FkId}")));
|
||||
}
|
||||
await cnx.IntegrityCheck();
|
||||
await cnx.ForeignKeyCheck();
|
||||
|
||||
await cnx.ExecuteBatch("VACUUM");
|
||||
await cnx.ExecuteBatch($"PRAGMA schema_version = {toVersion * 100 + VersionOffset}");
|
||||
|
||||
@@ -135,11 +135,39 @@ namespace Elwig.Helpers {
|
||||
return await cmd.ExecuteScalarAsync();
|
||||
}
|
||||
|
||||
public static async Task<(string Table, long RowId, string Parent, long FkId)[]> ForeignKeyCheck(this SqliteConnection cnx) {
|
||||
public static async Task QuickCheck(this SqliteConnection cnx) {
|
||||
using var cmd = cnx.CreateCommand();
|
||||
cmd.CommandText = "PRAGMA quick_check";
|
||||
using var reader = await cmd.ExecuteReaderAsync();
|
||||
var list = new List<string>();
|
||||
while (await reader.ReadAsync()) {
|
||||
list.Add(reader.GetString(0));
|
||||
}
|
||||
|
||||
if (list.Count != 1 || list[0] != "ok") {
|
||||
throw new FileFormatException($"Integrity problems ({list.Count}):\n" + string.Join("\n", list.Take(50)));
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task IntegrityCheck(this SqliteConnection cnx) {
|
||||
using var cmd = cnx.CreateCommand();
|
||||
cmd.CommandText = "PRAGMA integrity_check";
|
||||
using var reader = await cmd.ExecuteReaderAsync();
|
||||
var list = new List<string>();
|
||||
while (await reader.ReadAsync()) {
|
||||
list.Add(reader.GetString(0));
|
||||
}
|
||||
|
||||
if (list.Count != 1 || list[0] != "ok") {
|
||||
throw new FileFormatException($"Integrity problems ({list.Count}):\n" + string.Join("\n", list.Take(50)));
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task ForeignKeyCheck(this SqliteConnection cnx) {
|
||||
using var cmd = cnx.CreateCommand();
|
||||
cmd.CommandText = "PRAGMA foreign_key_check";
|
||||
using var reader = await cmd.ExecuteReaderAsync();
|
||||
var list = new List<(string, long, string, long)>();
|
||||
var list = new List<(string Table, long RowId, string Parent, long FkId)>();
|
||||
while (await reader.ReadAsync()) {
|
||||
var table = reader.GetString(0);
|
||||
var rowid = reader.GetInt64(1);
|
||||
@@ -147,7 +175,11 @@ namespace Elwig.Helpers {
|
||||
var fkid = reader.GetInt64(3);
|
||||
list.Add((table, rowid, parent, fkid));
|
||||
}
|
||||
return [.. list];
|
||||
|
||||
if (list.Count > 0) {
|
||||
throw new InvalidDataException($"Foreign key violations ({list.Count}):\n" + string.Join("\n", list.Take(50)
|
||||
.Select(v => $"{v.Table} - {v.RowId} - {v.Parent} - {v.FkId}")));
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<T> Join<T>(this IEnumerable<T> src, Func<T> separatorFactory) {
|
||||
|
||||
Reference in New Issue
Block a user