189 lines
8.7 KiB
C#
189 lines
8.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.IO.Compression;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Elwig.Helpers.Export {
|
|
public static class Database {
|
|
|
|
public static async Task ExportSqlite(string filename, bool zipFile) {
|
|
if (zipFile) {
|
|
File.Delete(filename);
|
|
using var zip = ZipFile.Open(filename, ZipArchiveMode.Create);
|
|
await zip.CheckIntegrity();
|
|
var db = zip.CreateEntryFromFile(App.Config.DatabaseFile, "database.sqlite3", CompressionLevel.SmallestSize);
|
|
} else {
|
|
File.Copy(App.Config.DatabaseFile, filename, true);
|
|
}
|
|
}
|
|
|
|
public static async Task ExportSql(string filename, bool zipFile) {
|
|
if (zipFile) {
|
|
File.Delete(filename);
|
|
using var zip = ZipFile.Open(filename, ZipArchiveMode.Create);
|
|
var entry = zip.CreateEntry("database.sql", CompressionLevel.SmallestSize);
|
|
using var stream = entry.Open();
|
|
using var writer = new StreamWriter(stream, Utils.UTF8);
|
|
await ExportSql(writer);
|
|
} else {
|
|
using var stream = File.OpenWrite(filename);
|
|
using var writer = new StreamWriter(stream, Utils.UTF8);
|
|
await ExportSql(writer);
|
|
}
|
|
}
|
|
|
|
public static async Task ExportSql(StreamWriter writer) {
|
|
using var cnx = await AppDbContext.ConnectAsync();
|
|
|
|
var tables = new List<(string Name, string Sql)>();
|
|
using (var cmd = cnx.CreateCommand()) {
|
|
cmd.CommandText = "SELECT name, sql FROM sqlite_schema WHERE type = 'table'";
|
|
using var reader = await cmd.ExecuteReaderAsync();
|
|
while (await reader.ReadAsync()) {
|
|
tables.Add((reader.GetString(0), reader.GetString(1)));
|
|
}
|
|
}
|
|
|
|
var applId = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA application_id") ?? 0;
|
|
var userVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA user_version") ?? 0;
|
|
var schemaVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA schema_version") ?? 0;
|
|
|
|
await writer.WriteLineAsync($"-- Elwig database dump, {DateTime.Now:yyyy-MM-dd, HH:mm:ss}");
|
|
await writer.WriteLineAsync($"-- {Environment.MachineName}, Zwst. {App.BranchName}, {App.Client.Name}");
|
|
await writer.WriteLineAsync("BEGIN TRANSACTION;");
|
|
await writer.WriteLineAsync("PRAGMA foreign_keys=OFF;");
|
|
await writer.WriteLineAsync($"PRAGMA application_id=0x{applId:X8};");
|
|
await writer.WriteLineAsync($"PRAGMA user_version=0x{userVers:X8};");
|
|
|
|
foreach (var t in tables) {
|
|
await writer.WriteAsync(t.Sql);
|
|
await writer.WriteLineAsync(";");
|
|
|
|
var columnNames = new List<string>();
|
|
using (var cmd = cnx.CreateCommand()) {
|
|
cmd.CommandText = $"PRAGMA table_info({t.Name})";
|
|
using var reader = await cmd.ExecuteReaderAsync();
|
|
while (await reader.ReadAsync()) {
|
|
columnNames.Add(reader.GetString(1));
|
|
}
|
|
}
|
|
|
|
using (var cmd = cnx.CreateCommand()) {
|
|
cmd.CommandText = $"SELECT {string.Join(',', columnNames)} FROM {t.Name}";
|
|
using var reader = await cmd.ExecuteReaderAsync();
|
|
var columns = await reader.GetColumnSchemaAsync();
|
|
var values = new object[reader.FieldCount];
|
|
while (await reader.ReadAsync()) {
|
|
await writer.WriteAsync($"INSERT INTO {t.Name} VALUES (");
|
|
|
|
reader.GetValues(values);
|
|
for (int i = 0; i < columns.Count; i++) {
|
|
var c = columns[i];
|
|
var v = values[i];
|
|
if (i > 0) await writer.WriteAsync(",");
|
|
if (v == null || v is DBNull) {
|
|
await writer.WriteAsync("NULL");
|
|
} else if (c.DataTypeName == "TEXT") {
|
|
await writer.WriteAsync($"'{v.ToString()?.Replace("'", "''")}'");
|
|
} else {
|
|
await writer.WriteAsync(v.ToString()?.Replace(',', '.'));
|
|
}
|
|
}
|
|
|
|
await writer.WriteLineAsync(");");
|
|
}
|
|
}
|
|
}
|
|
|
|
using (var cmd = cnx.CreateCommand()) {
|
|
cmd.CommandText = "SELECT sql FROM sqlite_schema WHERE type != 'table' AND sql IS NOT NULL";
|
|
using var reader = await cmd.ExecuteReaderAsync();
|
|
while (await reader.ReadAsync()) {
|
|
await writer.WriteAsync(reader.GetString(0));
|
|
await writer.WriteLineAsync(";");
|
|
}
|
|
}
|
|
|
|
await writer.WriteLineAsync($"PRAGMA schema_version={schemaVers};");
|
|
await writer.WriteLineAsync("PRAGMA foreign_keys=ON;");
|
|
await writer.WriteLineAsync("COMMIT;");
|
|
await writer.WriteLineAsync("VACUUM;");
|
|
}
|
|
|
|
public static async Task Import(string filename) {
|
|
if (filename.EndsWith(".sql")) {
|
|
await ImportSql(filename, false);
|
|
} else if (filename.EndsWith(".sql.zip")) {
|
|
await ImportSql(filename, true);
|
|
} else if (filename.EndsWith(".sqlite3")) {
|
|
await ImportSqlite(filename, false);
|
|
} else if (filename.EndsWith(".sqlite3.zip")) {
|
|
await ImportSqlite(filename, true);
|
|
} else {
|
|
throw new ArgumentException($"Unknown file extension for importing: '{filename}'");
|
|
}
|
|
}
|
|
|
|
public static async Task ImportSql(string filename, bool zipFile = false) {
|
|
if (zipFile) {
|
|
using var zip = ZipFile.Open(filename, ZipArchiveMode.Read);
|
|
await zip.CheckIntegrity();
|
|
foreach (var entry in zip.Entries) {
|
|
if (entry.Name.EndsWith(".sql")) {
|
|
using var stream = entry.Open();
|
|
using var reader = new StreamReader(stream, Utils.UTF8);
|
|
await ImportSql(reader);
|
|
return;
|
|
}
|
|
}
|
|
throw new FileFormatException("ZIP archive has to contain at least one .sql file");
|
|
} else {
|
|
using var stream = File.Open(filename, FileMode.Open);
|
|
using var reader = new StreamReader(stream, Utils.UTF8);
|
|
await ImportSql(reader);
|
|
}
|
|
}
|
|
|
|
public static async Task ImportSqlite(string filename, bool zipFile = false) {
|
|
if (zipFile) {
|
|
var newName = Path.ChangeExtension(App.Config.DatabaseFile, ".new.sqlite3");
|
|
try {
|
|
using var zip = ZipFile.Open(filename, ZipArchiveMode.Read);
|
|
await zip.CheckIntegrity();
|
|
foreach (var entry in zip.Entries) {
|
|
if (entry.Name.EndsWith(".sqlite3")) {
|
|
entry.ExtractToFile(newName);
|
|
await ImportSqlite(newName);
|
|
return;
|
|
}
|
|
}
|
|
throw new FileFormatException("ZIP archive has to contain at least one .sqlite3 file");
|
|
} finally {
|
|
if (File.Exists(newName)) File.Delete(newName);
|
|
}
|
|
}
|
|
|
|
var oldName = Path.ChangeExtension(App.Config.DatabaseFile, ".old.sqlite3");
|
|
File.Move(App.Config.DatabaseFile, oldName, true);
|
|
File.Move(filename, App.Config.DatabaseFile, false);
|
|
|
|
using var cnx = await AppDbContext.ConnectAsync();
|
|
await AppDbContext.ExecuteBatch(cnx, "VACUUM");
|
|
}
|
|
|
|
public static async Task ImportSql(StreamReader reader) {
|
|
var newName = Path.ChangeExtension(App.Config.DatabaseFile, ".new.sqlite3");
|
|
File.Delete(newName);
|
|
try {
|
|
using (var cnx = await AppDbContext.ConnectAsync($"Data Source=\"{newName}\"; Mode=ReadWriteCreate; Foreign Keys=False; Cache=Default; Pooling=False")) {
|
|
await AppDbContext.ExecuteBatch(cnx, await reader.ReadToEndAsync());
|
|
}
|
|
await ImportSqlite(newName);
|
|
} finally {
|
|
if (File.Exists(newName)) File.Delete(newName);
|
|
}
|
|
}
|
|
}
|
|
}
|