/Horizon/Database/SnapshotDatabase.cs/SnapshotDatabase.cs |
@@ -6,6 +6,8 @@ |
using System.Globalization; |
using System.IO; |
using System.IO.Compression; |
using System.Runtime.CompilerServices; |
using System.Security.AccessControl; |
using System.Security.Cryptography; |
using System.Threading; |
using System.Threading.Tasks; |
@@ -45,6 +47,12 @@ |
private const string RemoveSnapshotFromHashSql = |
"DELETE FROM \"Snapshots\" WHERE Hash = @hash"; |
|
private const string GetTransferSnapshotFromHashSql = |
"SELECT \"Name\", \"Path\", \"Time\", \"Data\", \"Shot\", \"Color\", \"Hash\", \"Note\" FROM \"Snapshots\" WHERE Hash = @hash"; |
|
private const string SetTransferSnapshotSql = |
"INSERT INTO \"Snapshots\" ( \"Name\", \"Path\", \"Time\", \"Data\", \"Shot\", \"Color\", \"Hash\", \"Note\" ) VALUES ( @name, @path, @time, zeroblob(@dataLength), zeroblob(@shotLength), @color, @hash, @note )"; |
|
private const string RemoveScreenshotFromHashSql = |
"UPDATE \"Snapshots\" SET Shot = null WHERE Hash = @hash"; |
|
@@ -89,6 +97,8 @@ |
|
public event EventHandler<SnapshotCreateEventArgs> SnapshotCreate; |
|
public event EventHandler<SnapshotCreateEventArgs> SnapshotTransferReceived; |
|
public event EventHandler<SnapshotRevertEventArgs> SnapshotRevert; |
|
#endregion |
@@ -96,22 +106,33 @@ |
#region Private Delegates, Events, Enums, Properties, Indexers and Fields |
|
private readonly CancellationTokenSource _cancellationTokenSource; |
private readonly SemaphoreSlim _databaseLock; |
private readonly SQLiteConnectionStringBuilder _sqliteConnectionStringBuilder; |
|
private SemaphoreSlim _snapshotSemaphore; |
|
#endregion |
|
#region Constructors, Destructors and Finalizers |
|
public SnapshotDatabase() |
private SnapshotDatabase() |
{ |
_cancellationTokenSource = new CancellationTokenSource(); |
_cancellationToken = _cancellationTokenSource.Token; |
Directory.CreateDirectory(Constants.DatabaseDirectory); |
|
_snapshotSemaphore = new SemaphoreSlim(1, 1); |
_databaseLock = new SemaphoreSlim(1, 1); |
|
Directory.CreateDirectory(Constants.DatabaseDirectory); |
_sqliteConnectionStringBuilder = new SQLiteConnectionStringBuilder |
{ |
ConnectionString = DatabaseConnectionString |
}; |
} |
|
public SnapshotDatabase(CancellationToken cancellationToken) : this() |
{ |
_cancellationTokenSource = new CancellationTokenSource(); |
var localCancellationToken = _cancellationTokenSource.Token; |
var combinedCancellationTokenSource = |
CancellationTokenSource.CreateLinkedTokenSource(localCancellationToken, cancellationToken); |
_cancellationToken = combinedCancellationTokenSource.Token; |
|
CreateDatabase(_cancellationToken).ContinueWith(async createDatabaseTask => |
{ |
try |
@@ -122,14 +143,14 @@ |
{ |
await SetAutoVacuum(_cancellationToken); |
} |
catch |
catch (Exception exception) |
{ |
Log.Error("Unable to set auto vacuum for database."); |
Log.Error(exception, "Unable to set auto vacuum for database."); |
} |
} |
catch |
catch (Exception exception) |
{ |
Log.Error("Unable to create database;"); |
Log.Error(exception, "Unable to create database;"); |
} |
}).Wait(_cancellationToken); |
} |
@@ -137,9 +158,6 @@ |
public void Dispose() |
{ |
_cancellationTokenSource.Cancel(); |
|
_snapshotSemaphore?.Dispose(); |
_snapshotSemaphore = null; |
} |
|
#endregion |
@@ -146,7 +164,7 @@ |
|
#region Public Methods |
|
public async Task DeleteScreenshot(string hash, CancellationToken cancellationToken) |
public async Task DeleteScreenshotAsync(string hash, CancellationToken cancellationToken) |
{ |
var connectionString = new SQLiteConnectionStringBuilder |
{ |
@@ -153,6 +171,9 @@ |
ConnectionString = DatabaseConnectionString |
}; |
|
await _databaseLock.WaitAsync(cancellationToken); |
try |
{ |
using (var sqliteConnection = new SQLiteConnection(connectionString.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
@@ -163,8 +184,6 @@ |
using (var sqliteCommand = |
new SQLiteCommand(RemoveScreenshotFromHashSql, sqliteConnection, dbTransaction)) |
{ |
try |
{ |
sqliteCommand.Parameters.AddRange(new[] |
{ |
new SQLiteParameter("@hash", hash) |
@@ -172,6 +191,8 @@ |
|
sqliteCommand.Prepare(); |
|
try |
{ |
await sqliteCommand.ExecuteNonQueryAsync(cancellationToken); |
|
dbTransaction.Commit(); |
@@ -186,16 +207,19 @@ |
} |
} |
} |
finally |
{ |
_databaseLock.Release(); |
} |
} |
|
public async Task NormalizeTime(string hash, CancellationToken cancellationToken) |
public async Task NormalizeTimeAsync(string hash, CancellationToken cancellationToken) |
{ |
var connectionString = new SQLiteConnectionStringBuilder |
await _databaseLock.WaitAsync(cancellationToken); |
try |
{ |
ConnectionString = DatabaseConnectionString |
}; |
|
using (var sqliteConnection = |
new SQLiteConnection(connectionString.ConnectionString)) |
new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
|
@@ -237,7 +261,8 @@ |
{ |
writeSQLiteCommand.Parameters.AddRange(new[] |
{ |
new SQLiteParameter("@time", dateTime.ToString("yyyy-MM-ddTHH:mm:ss.fff")), |
new SQLiteParameter("@time", |
dateTime.ToString("yyyy-MM-ddTHH:mm:ss.fff")), |
new SQLiteParameter("@hash", hash) |
}); |
|
@@ -260,15 +285,18 @@ |
} |
} |
} |
finally |
{ |
_databaseLock.Release(); |
} |
} |
|
public async Task<long> CountSnapshots(CancellationToken cancellationToken) |
public async Task<long> CountSnapshotsAsync(CancellationToken cancellationToken) |
{ |
var connectionString = new SQLiteConnectionStringBuilder |
await _databaseLock.WaitAsync(cancellationToken); |
try |
{ |
ConnectionString = DatabaseConnectionString |
}; |
|
using (var sqliteConnection = new SQLiteConnection(connectionString.ConnectionString)) |
using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
|
@@ -297,16 +325,19 @@ |
} |
} |
} |
finally |
{ |
_databaseLock.Release(); |
} |
} |
|
public async Task<IEnumerable<Snapshot>> LoadSnapshots(CancellationToken cancellationToken) |
public async IAsyncEnumerable<Snapshot> LoadSnapshotsAsync([EnumeratorCancellation] CancellationToken cancellationToken) |
{ |
var connectionString = new SQLiteConnectionStringBuilder |
await _databaseLock.WaitAsync(cancellationToken); |
try |
{ |
ConnectionString = DatabaseConnectionString |
}; |
|
using (var sqliteConnection = |
new SQLiteConnection(connectionString.ConnectionString)) |
new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
|
@@ -317,7 +348,7 @@ |
|
using (var sqlDataReader = await sqliteCommand.ExecuteReaderAsync(cancellationToken)) |
{ |
var snapshots = new List<Snapshot>(); |
//var snapshots = new List<Snapshot>(); |
while (await sqlDataReader.ReadAsync(cancellationToken)) |
{ |
var name = (string)sqlDataReader["Name"]; |
@@ -342,29 +373,318 @@ |
} |
} |
|
snapshots.Add(new Snapshot(name, path, time, hash, color)); |
yield return new Snapshot(name, path, time, hash, color); |
} |
} |
} |
} |
} |
finally |
{ |
_databaseLock.Release(); |
} |
} |
|
return snapshots; |
public async Task ApplyTransferSnapshotAsync(TransferSnapshot transferSnapshot, CancellationToken cancellationToken) |
{ |
await _databaseLock.WaitAsync(cancellationToken); |
try |
{ |
using (var sqliteConnection = |
new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
|
var color = Color.Empty; |
using (var dbTransaction = sqliteConnection.BeginTransaction()) |
{ |
try |
{ |
using (var dataMemoryStream = new MemoryStream()) |
{ |
using (var dataZipStream = |
new GZipStream(dataMemoryStream, CompressionMode.Compress, true)) |
{ |
dataMemoryStream.Position = 0L; |
await dataZipStream.WriteAsync(transferSnapshot.Data, 0, |
transferSnapshot.Data.Length, cancellationToken); |
dataZipStream.Close(); |
|
using (var bitmapMemoryStream = new MemoryStream()) |
{ |
bitmapMemoryStream.Position = 0L; |
using (var bitmapZipStream = |
new GZipStream(bitmapMemoryStream, CompressionMode.Compress, |
true)) |
{ |
using (var transferImageStream = new MemoryStream(transferSnapshot.Shot)) |
{ |
transferImageStream.Position = 0L; |
await transferImageStream.CopyToAsync(bitmapZipStream); |
bitmapZipStream.Close(); |
bitmapMemoryStream.Position = 0L; |
|
var a = bitmapMemoryStream.ToArray(); |
|
// Insert the file change. |
using (var sqliteCommand = |
new SQLiteCommand(SetTransferSnapshotSql, sqliteConnection, |
dbTransaction)) |
{ |
sqliteCommand.Parameters.AddRange(new[] |
{ |
new SQLiteParameter("@name", transferSnapshot.Name), |
new SQLiteParameter("@path", transferSnapshot.Path), |
new SQLiteParameter("@time", transferSnapshot.Time), |
new SQLiteParameter("@dataLength", |
dataMemoryStream.Length), |
new SQLiteParameter("@shotLength", |
bitmapMemoryStream.Length), |
new SQLiteParameter("@hash", transferSnapshot.Hash), |
new SQLiteParameter("@note", transferSnapshot.Note) |
}); |
|
var numeric = transferSnapshot.Color; |
switch (numeric) |
{ |
case 0: |
sqliteCommand.Parameters.Add( |
new SQLiteParameter("@color", DBNull.Value)); |
color = Color.Empty; |
break; |
default: |
sqliteCommand.Parameters.Add( |
new SQLiteParameter("@color", numeric)); |
color = Color.FromArgb(transferSnapshot.Color); |
break; |
} |
|
sqliteCommand.Prepare(); |
|
await sqliteCommand.ExecuteNonQueryAsync(cancellationToken); |
} |
|
// Insert the data blobs. |
using (var sqliteCommand = |
new SQLiteCommand(GetLastRowInsertSql, sqliteConnection, |
dbTransaction)) |
{ |
sqliteCommand.Prepare(); |
|
var rowId = |
(long)await sqliteCommand.ExecuteScalarAsync( |
cancellationToken); |
|
using (var sqliteBlob = |
SQLiteBlob.Create(sqliteConnection, "main", |
"Snapshots", |
"Data", |
rowId, |
false)) |
{ |
var fileMemoryStreamData = dataMemoryStream.ToArray(); |
|
sqliteBlob.Write(fileMemoryStreamData, |
fileMemoryStreamData.Length, |
0); |
} |
|
using (var sqliteBlob = |
SQLiteBlob.Create(sqliteConnection, "main", |
"Snapshots", |
"Shot", |
rowId, |
false)) |
{ |
var bitmapMemoryStreamData = |
bitmapMemoryStream.ToArray(); |
|
sqliteBlob.Write(bitmapMemoryStreamData, |
bitmapMemoryStreamData.Length, |
0); |
} |
} |
|
public async Task CreateSnapshot(string name, string path, Color color, CancellationToken cancellationToken) |
dbTransaction.Commit(); |
|
SnapshotTransferReceived?.Invoke(this, |
new SnapshotCreateSuccessEventArgs( |
transferSnapshot.Name, |
transferSnapshot.Time, |
transferSnapshot.Path, |
color, |
transferSnapshot.Hash) |
); |
} |
} |
} |
} |
} |
} |
catch (SQLiteException exception) |
{ |
await _snapshotSemaphore.WaitAsync(cancellationToken); |
dbTransaction.Rollback(); |
|
var connectionString = new SQLiteConnectionStringBuilder |
if (exception.ResultCode != SQLiteErrorCode.Constraint) |
{ |
ConnectionString = DatabaseConnectionString |
}; |
SnapshotTransferReceived?.Invoke(this, |
new SnapshotCreateFailureEventArgs( |
transferSnapshot.Name, |
transferSnapshot.Path, |
color, |
exception) |
); |
} |
|
throw; |
} |
catch (Exception exception) |
{ |
dbTransaction.Rollback(); |
|
SnapshotTransferReceived?.Invoke(this, |
new SnapshotCreateFailureEventArgs( |
transferSnapshot.Name, |
transferSnapshot.Path, |
color, |
exception) |
); |
|
throw; |
} |
} |
} |
} |
finally |
{ |
_databaseLock.Release(); |
} |
} |
|
|
public async Task<TransferSnapshot> GenerateTransferSnapshotAsync(string hash, CancellationToken cancellationToken) |
{ |
await _databaseLock.WaitAsync(cancellationToken); |
try |
{ |
using (var sqliteConnection = |
new SQLiteConnection(connectionString.ConnectionString)) |
new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
|
// Insert the file change. |
using (var sqliteCommand = new SQLiteCommand(GetTransferSnapshotFromHashSql, sqliteConnection)) |
{ |
|
sqliteCommand.Parameters.AddRange(new[] |
{ |
new SQLiteParameter("@hash", hash) |
}); |
|
sqliteCommand.Prepare(); |
|
using (var sqlDataReader = await sqliteCommand.ExecuteReaderAsync(cancellationToken)) |
{ |
//var snapshots = new List<Snapshot>(); |
while (await sqlDataReader.ReadAsync(cancellationToken)) |
{ |
var name = (string)sqlDataReader["Name"]; |
var path = (string)sqlDataReader["Path"]; |
var time = (string)sqlDataReader["Time"]; |
|
var color = Color.Empty; |
|
if (!(sqlDataReader["Color"] is DBNull)) |
{ |
var dbColor = Convert.ToInt32(sqlDataReader["Color"]); |
|
switch (dbColor) |
{ |
case 0: |
color = Color.Empty; |
break; |
default: |
color = Color.FromArgb(dbColor); |
break; |
} |
} |
|
var note = string.Empty; |
|
if (!(sqlDataReader["Note"] is DBNull)) |
{ |
note = (string)sqlDataReader["Note"]; |
} |
|
Bitmap shot = null; |
|
if (!(sqlDataReader["Shot"] is DBNull)) |
{ |
var readStream = sqlDataReader.GetStream(4); |
|
readStream.Position = 0L; |
|
using (var zipStream = new GZipStream(readStream, CompressionMode.Decompress)) |
{ |
using (var image = Image.FromStream(zipStream)) |
{ |
shot = new Bitmap(image); |
} |
} |
} |
|
byte[] data = null; |
if (!(sqlDataReader["Data"] is DBNull)) |
{ |
using (var readStream = sqlDataReader.GetStream(3)) |
{ |
using (var memoryStream = new MemoryStream()) |
{ |
readStream.Position = 0L; |
|
await readStream.CopyToAsync(memoryStream); |
|
memoryStream.Position = 0L; |
|
using (var zipStream = new GZipStream(memoryStream, CompressionMode.Decompress)) |
{ |
// Do not dispose the returned stream and leave it up to callers to dispose. |
var outputStream = new MemoryStream(); |
|
await zipStream.CopyToAsync(outputStream); |
|
outputStream.Position = 0L; |
|
data = outputStream.ToArray(); |
} |
} |
} |
} |
|
return new TransferSnapshot(name, path, time, hash, color, shot, note, data); |
} |
} |
} |
} |
} |
finally |
{ |
_databaseLock.Release(); |
} |
|
return null; |
} |
|
public async Task CreateSnapshotAsync(string name, string path, Color color, CancellationToken cancellationToken) |
{ |
await _databaseLock.WaitAsync(cancellationToken); |
try |
{ |
using (var sqliteConnection = |
new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
|
using (var dbTransaction = sqliteConnection.BeginTransaction()) |
{ |
try |
@@ -419,7 +739,7 @@ |
{ |
case 0: |
sqliteCommand.Parameters.Add( |
new SQLiteParameter("@color", null)); |
new SQLiteParameter("@color", DBNull.Value)); |
break; |
default: |
sqliteCommand.Parameters.Add( |
@@ -443,13 +763,15 @@ |
(long)await sqliteCommand.ExecuteScalarAsync(cancellationToken); |
|
using (var sqliteBlob = |
SQLiteBlob.Create(sqliteConnection, "main", "Snapshots", "Data", |
SQLiteBlob.Create(sqliteConnection, "main", "Snapshots", |
"Data", |
rowId, |
false)) |
{ |
var fileMemoryStreamData = fileMemoryStream.ToArray(); |
|
sqliteBlob.Write(fileMemoryStreamData, fileMemoryStreamData.Length, |
sqliteBlob.Write(fileMemoryStreamData, |
fileMemoryStreamData.Length, |
0); |
} |
} |
@@ -481,30 +803,28 @@ |
{ |
dbTransaction.Rollback(); |
|
SnapshotCreate?.Invoke(this, new SnapshotCreateFailureEventArgs(name, path, color, exception)); |
SnapshotCreate?.Invoke(this, |
new SnapshotCreateFailureEventArgs(name, path, color, exception)); |
|
throw; |
} |
} |
} |
} |
finally |
{ |
_snapshotSemaphore.Release(); |
_databaseLock.Release(); |
} |
} |
} |
} |
|
public async Task CreateSnapshot(string name, string path, |
public async Task CreateSnapshotAsync(string name, string path, |
Bitmap shot, Color color, CancellationToken cancellationToken) |
{ |
await _snapshotSemaphore.WaitAsync(cancellationToken); |
|
var connectionString = new SQLiteConnectionStringBuilder |
await _databaseLock.WaitAsync(cancellationToken); |
try |
{ |
ConnectionString = DatabaseConnectionString |
}; |
|
using (var sqliteConnection = |
new SQLiteConnection(connectionString.ConnectionString)) |
new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
|
@@ -574,7 +894,7 @@ |
{ |
case 0: |
sqliteCommand.Parameters.Add( |
new SQLiteParameter("@color", null)); |
new SQLiteParameter("@color", DBNull.Value)); |
break; |
default: |
sqliteCommand.Parameters.Add( |
@@ -599,7 +919,8 @@ |
cancellationToken); |
|
using (var sqliteBlob = |
SQLiteBlob.Create(sqliteConnection, "main", "Snapshots", |
SQLiteBlob.Create(sqliteConnection, "main", |
"Snapshots", |
"Data", |
rowId, |
false)) |
@@ -612,12 +933,14 @@ |
} |
|
using (var sqliteBlob = |
SQLiteBlob.Create(sqliteConnection, "main", "Snapshots", |
SQLiteBlob.Create(sqliteConnection, "main", |
"Snapshots", |
"Shot", |
rowId, |
false)) |
{ |
var bitmapMemoryStreamData = bitmapMemoryStream.ToArray(); |
var bitmapMemoryStreamData = |
bitmapMemoryStream.ToArray(); |
|
sqliteBlob.Write(bitmapMemoryStreamData, |
bitmapMemoryStreamData.Length, |
@@ -654,26 +977,26 @@ |
{ |
dbTransaction.Rollback(); |
|
SnapshotCreate?.Invoke(this, new SnapshotCreateFailureEventArgs(name, path, color, exception)); |
SnapshotCreate?.Invoke(this, |
new SnapshotCreateFailureEventArgs(name, path, color, exception)); |
|
throw; |
} |
} |
} |
} |
finally |
{ |
_snapshotSemaphore.Release(); |
_databaseLock.Release(); |
} |
} |
} |
} |
|
public async Task SaveFile(string path, string hash, CancellationToken cancellationToken) |
public async Task SaveFileAsync(string path, string hash, CancellationToken cancellationToken) |
{ |
var connectionString = new SQLiteConnectionStringBuilder |
await _databaseLock.WaitAsync(cancellationToken); |
try |
{ |
ConnectionString = DatabaseConnectionString |
}; |
|
using (var sqliteConnection = new SQLiteConnection(connectionString.ConnectionString)) |
using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
|
@@ -720,18 +1043,19 @@ |
} |
} |
} |
|
public async Task RevertFile(string name, string hash, CancellationToken cancellationToken, bool atomic = true) |
finally |
{ |
await _snapshotSemaphore.WaitAsync(cancellationToken); |
_databaseLock.Release(); |
} |
} |
|
var connectionString = new SQLiteConnectionStringBuilder |
public async Task RevertFileAsync(string name, string hash, CancellationToken cancellationToken, bool atomic = true) |
{ |
ConnectionString = DatabaseConnectionString |
}; |
|
using (var sqliteConnection = new SQLiteConnection(connectionString.ConnectionString)) |
await _databaseLock.WaitAsync(cancellationToken); |
try |
{ |
using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
|
// Insert the file change. |
@@ -765,7 +1089,7 @@ |
{ |
case true: |
// Atomic |
var temp = Path.Combine(Path.GetDirectoryName(path), |
var temp = Path.Combine(dir, |
$"{Path.GetFileName(path)}.temp"); |
|
using (var readStream = sqlDataReader.GetStream(2)) |
@@ -777,17 +1101,28 @@ |
using (var zipStream = |
new GZipStream(readStream, CompressionMode.Decompress)) |
{ |
zipStream.CopyTo(fileStream); |
await zipStream.CopyToAsync(fileStream); |
zipStream.Close(); |
} |
|
fileStream.Close(); |
} |
|
readStream.Close(); |
} |
|
try |
{ |
if (!File.Exists(path)) |
{ |
File.Create(path).Close(); |
} |
|
File.Replace(temp, path, null, true); |
} |
catch |
catch(Exception replaceException) |
{ |
Log.Warning(replaceException, "Failed to copy temporary file."); |
try |
{ |
File.Delete(temp); |
@@ -818,8 +1153,13 @@ |
new GZipStream(readStream, CompressionMode.Decompress)) |
{ |
await zipStream.CopyToAsync(fileStream); |
zipStream.Close(); |
} |
|
fileStream.Close(); |
} |
|
readStream.Close(); |
} |
|
|
@@ -836,22 +1176,21 @@ |
|
throw; |
} |
} |
} |
} |
finally |
{ |
_snapshotSemaphore.Release(); |
_databaseLock.Release(); |
} |
} |
} |
} |
|
public async Task RemoveFileFast(IEnumerable<string> hashes, CancellationToken cancellationToken) |
public async Task RemoveFileFastAsync(IEnumerable<string> hashes, CancellationToken cancellationToken) |
{ |
var connectionString = new SQLiteConnectionStringBuilder |
await _databaseLock.WaitAsync(cancellationToken); |
try |
{ |
ConnectionString = DatabaseConnectionString |
}; |
|
using (var sqliteConnection = new SQLiteConnection(connectionString.ConnectionString)) |
using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
|
@@ -893,15 +1232,18 @@ |
} |
} |
} |
finally |
{ |
_databaseLock.Release(); |
} |
} |
|
public async Task RemoveFile(string hash, CancellationToken cancellationToken) |
public async Task RemoveFileAsync(string hash, CancellationToken cancellationToken) |
{ |
var connectionString = new SQLiteConnectionStringBuilder |
await _databaseLock.WaitAsync(cancellationToken); |
try |
{ |
ConnectionString = DatabaseConnectionString |
}; |
|
using (var sqliteConnection = new SQLiteConnection(connectionString.ConnectionString)) |
using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
|
@@ -911,8 +1253,6 @@ |
using (var sqliteCommand = |
new SQLiteCommand(RemoveSnapshotFromHashSql, sqliteConnection, dbTransaction)) |
{ |
try |
{ |
sqliteCommand.Parameters.AddRange(new[] |
{ |
new SQLiteParameter("@hash", hash) |
@@ -920,6 +1260,8 @@ |
|
sqliteCommand.Prepare(); |
|
try |
{ |
await sqliteCommand.ExecuteNonQueryAsync(cancellationToken); |
|
dbTransaction.Commit(); |
@@ -934,16 +1276,20 @@ |
} |
} |
} |
finally |
{ |
_databaseLock.Release(); |
} |
} |
|
public async Task UpdateColor(string hash, Color color, CancellationToken cancellationToken) |
public async Task UpdateColorAsync(string hash, Color color, CancellationToken cancellationToken) |
{ |
var connectionString = new SQLiteConnectionStringBuilder |
{ |
ConnectionString = DatabaseConnectionString |
}; |
await _databaseLock.WaitAsync(cancellationToken); |
|
using (var sqliteConnection = new SQLiteConnection(connectionString.ConnectionString)) |
try |
{ |
using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
|
using (var dbTransaction = sqliteConnection.BeginTransaction()) |
@@ -952,16 +1298,28 @@ |
using (var sqliteCommand = |
new SQLiteCommand(UpdateColorFromHashSql, sqliteConnection, dbTransaction)) |
{ |
try |
{ |
sqliteCommand.Parameters.AddRange(new[] |
{ |
new SQLiteParameter("@hash", hash), |
new SQLiteParameter("@color", color.ToArgb()) |
new SQLiteParameter("@hash", hash) |
}); |
|
var numeric = color.ToArgb(); |
switch (numeric) |
{ |
case 0: |
sqliteCommand.Parameters.Add( |
new SQLiteParameter("@color", DBNull.Value)); |
break; |
default: |
sqliteCommand.Parameters.Add( |
new SQLiteParameter("@color", numeric)); |
break; |
} |
|
sqliteCommand.Prepare(); |
|
try |
{ |
await sqliteCommand.ExecuteNonQueryAsync(cancellationToken); |
|
dbTransaction.Commit(); |
@@ -976,15 +1334,18 @@ |
} |
} |
} |
finally |
{ |
_databaseLock.Release(); |
} |
} |
|
public async Task RemoveColor(string hash, CancellationToken cancellationToken) |
public async Task RemoveColorAsync(string hash, CancellationToken cancellationToken) |
{ |
var connectionString = new SQLiteConnectionStringBuilder |
await _databaseLock.WaitAsync(cancellationToken); |
try |
{ |
ConnectionString = DatabaseConnectionString |
}; |
|
using (var sqliteConnection = new SQLiteConnection(connectionString.ConnectionString)) |
using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
|
@@ -994,8 +1355,6 @@ |
using (var sqliteCommand = |
new SQLiteCommand(RemoveColorFromHashSql, sqliteConnection, dbTransaction)) |
{ |
try |
{ |
sqliteCommand.Parameters.AddRange(new[] |
{ |
new SQLiteParameter("@hash", hash) |
@@ -1003,6 +1362,8 @@ |
|
sqliteCommand.Prepare(); |
|
try |
{ |
await sqliteCommand.ExecuteNonQueryAsync(cancellationToken); |
|
dbTransaction.Commit(); |
@@ -1017,15 +1378,18 @@ |
} |
} |
} |
finally |
{ |
_databaseLock.Release(); |
} |
} |
|
public async Task<SnapshotPreview> RetrievePreview(string hash, CancellationToken cancellationToken) |
public async Task<SnapshotPreview> RetrievePreviewAsync(string hash, CancellationToken cancellationToken) |
{ |
var connectionString = new SQLiteConnectionStringBuilder |
await _databaseLock.WaitAsync(cancellationToken); |
try |
{ |
ConnectionString = DatabaseConnectionString |
}; |
|
using (var sqliteConnection = new SQLiteConnection(connectionString.ConnectionString)) |
using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
|
@@ -1058,6 +1422,8 @@ |
|
readStream.Position = 0L; |
|
try |
{ |
using (var zipStream = new GZipStream(readStream, CompressionMode.Decompress)) |
{ |
using (var image = Image.FromStream(zipStream)) |
@@ -1066,6 +1432,12 @@ |
} |
} |
} |
catch (Exception exception) |
{ |
Log.Error(exception, $"Could not retrieve image preview for snapshot {hash}."); |
return null; |
} |
} |
|
return new SnapshotPreview(hash, shot, note); |
} |
@@ -1075,77 +1447,18 @@ |
} |
} |
} |
|
/* |
public MemoryStream RetrieveFileStream(string hash, CancellationToken cancellationToken) |
finally |
{ |
var connectionString = new SQLiteConnectionStringBuilder |
{ |
ConnectionString = DatabaseConnectionString |
}; |
|
using (var sqliteConnection = new SQLiteConnection(connectionString.ConnectionString)) |
{ |
|
sqliteConnection.Open(); |
|
// Insert the file change. |
using (var sqliteCommand = new SQLiteCommand(RetrieveDataFromHashSql, sqliteConnection)) |
{ |
sqliteCommand.Parameters.AddRange(new[] |
{ |
new SQLiteParameter("@hash", hash) |
}); |
|
sqliteCommand.Prepare(); |
|
using (var sqlDataReader = sqliteCommand.ExecuteReader()) |
{ |
|
while (sqlDataReader.Read()) |
{ |
using (var readStream = sqlDataReader.GetStream(1)) |
{ |
|
using (var memoryStream = new MemoryStream()) |
{ |
|
readStream.Position = 0L; |
|
readStream.CopyTo(memoryStream); |
|
memoryStream.Position = 0L; |
|
using (var zipStream = new GZipStream(memoryStream, CompressionMode.Decompress)) |
{ |
|
var outputStream = new MemoryStream(); |
|
zipStream.CopyTo(outputStream); |
|
outputStream.Position = 0L; |
|
return outputStream; |
_databaseLock.Release(); |
} |
} |
} |
} |
|
return null; |
} |
} |
} |
} |
*/ |
|
public async Task<MemoryStream> RetrieveFileStream(string hash, CancellationToken cancellationToken) |
public async Task<MemoryStream> RetrieveFileStreamAsync(string hash, CancellationToken cancellationToken) |
{ |
var connectionString = new SQLiteConnectionStringBuilder |
await _databaseLock.WaitAsync(cancellationToken); |
try |
{ |
ConnectionString = DatabaseConnectionString |
}; |
|
using (var sqliteConnection = new SQLiteConnection(connectionString.ConnectionString)) |
using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
|
@@ -1193,15 +1506,18 @@ |
} |
} |
} |
finally |
{ |
_databaseLock.Release(); |
} |
} |
|
public async Task RelocateFile(string hash, string path, CancellationToken cancellationToken) |
public async Task RelocateFileAsync(string hash, string path, CancellationToken cancellationToken) |
{ |
var connectionString = new SQLiteConnectionStringBuilder |
await _databaseLock.WaitAsync(cancellationToken); |
try |
{ |
ConnectionString = DatabaseConnectionString |
}; |
|
using (var sqliteConnection = new SQLiteConnection(connectionString.ConnectionString)) |
using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
|
@@ -1211,8 +1527,6 @@ |
using (var sqliteCommand = |
new SQLiteCommand(RelocateFileFromHashSql, sqliteConnection, dbTransaction)) |
{ |
try |
{ |
sqliteCommand.Parameters.AddRange(new[] |
{ |
new SQLiteParameter("@hash", hash), |
@@ -1221,6 +1535,8 @@ |
|
sqliteCommand.Prepare(); |
|
try |
{ |
await sqliteCommand.ExecuteNonQueryAsync(cancellationToken); |
|
dbTransaction.Commit(); |
@@ -1236,15 +1552,19 @@ |
} |
} |
|
public async Task UpdateNote(string hash, string note, CancellationToken cancellationToken) |
finally |
{ |
var connectionString = new SQLiteConnectionStringBuilder |
{ |
ConnectionString = DatabaseConnectionString |
}; |
_databaseLock.Release(); |
} |
} |
|
using (var sqliteConnection = new SQLiteConnection(connectionString.ConnectionString)) |
public async Task UpdateNoteAsync(string hash, string note, CancellationToken cancellationToken) |
{ |
await _databaseLock.WaitAsync(cancellationToken); |
try |
{ |
using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
|
using (var dbTransaction = sqliteConnection.BeginTransaction()) |
@@ -1253,8 +1573,6 @@ |
using (var sqliteCommand = |
new SQLiteCommand(UpdateNoteFromHashSql, sqliteConnection, dbTransaction)) |
{ |
try |
{ |
sqliteCommand.Parameters.AddRange(new[] |
{ |
new SQLiteParameter("@hash", hash), |
@@ -1263,6 +1581,8 @@ |
|
sqliteCommand.Prepare(); |
|
try |
{ |
await sqliteCommand.ExecuteNonQueryAsync(cancellationToken); |
|
dbTransaction.Commit(); |
@@ -1281,18 +1601,21 @@ |
} |
} |
} |
finally |
{ |
_databaseLock.Release(); |
} |
} |
|
public async Task<string> UpdateFile(string hash, byte[] data, CancellationToken cancellationToken) |
public async Task<string> UpdateFileAsync(string hash, byte[] data, CancellationToken cancellationToken) |
{ |
await _databaseLock.WaitAsync(cancellationToken); |
try |
{ |
using (var dataMemoryStream = new MemoryStream(data)) |
{ |
var connectionString = new SQLiteConnectionStringBuilder |
using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) |
{ |
ConnectionString = DatabaseConnectionString |
}; |
|
using (var sqliteConnection = new SQLiteConnection(connectionString.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
|
using (var dbTransaction = sqliteConnection.BeginTransaction()) |
@@ -1324,7 +1647,8 @@ |
|
// Insert the file change. |
using (var sqliteCommand = |
new SQLiteCommand(UpdateFileSql, sqliteConnection, dbTransaction)) |
new SQLiteCommand(UpdateFileSql, sqliteConnection, |
dbTransaction)) |
{ |
sqliteCommand.Parameters.AddRange(new[] |
{ |
@@ -1355,13 +1679,15 @@ |
{ |
if (sqlDataReader["id"] is long rowId) |
{ |
using (var sqliteBlob = SQLiteBlob.Create(sqliteConnection, |
using (var sqliteBlob = SQLiteBlob.Create( |
sqliteConnection, |
"main", |
"Snapshots", |
"Data", |
rowId, false)) |
{ |
var fileMemoryStreamData = fileMemoryStream.ToArray(); |
var fileMemoryStreamData = |
fileMemoryStream.ToArray(); |
|
sqliteBlob.Write(fileMemoryStreamData, |
fileMemoryStreamData.Length, |
@@ -1395,15 +1721,18 @@ |
} |
} |
} |
finally |
{ |
_databaseLock.Release(); |
} |
} |
|
public async Task UpdateHash(string from, string to, CancellationToken cancellationToken) |
public async Task UpdateHashAsync(string from, string to, CancellationToken cancellationToken) |
{ |
var connectionString = new SQLiteConnectionStringBuilder |
await _databaseLock.WaitAsync(cancellationToken); |
try |
{ |
ConnectionString = DatabaseConnectionString |
}; |
|
using (var sqliteConnection = new SQLiteConnection(connectionString.ConnectionString)) |
using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
|
@@ -1413,8 +1742,6 @@ |
using (var sqliteCommand = |
new SQLiteCommand(UpdateHashFromHashSql, sqliteConnection, dbTransaction)) |
{ |
try |
{ |
sqliteCommand.Parameters.AddRange(new[] |
{ |
new SQLiteParameter("@from", from), |
@@ -1423,6 +1750,8 @@ |
|
sqliteCommand.Prepare(); |
|
try |
{ |
await sqliteCommand.ExecuteNonQueryAsync(cancellationToken); |
|
dbTransaction.Commit(); |
@@ -1437,20 +1766,23 @@ |
} |
} |
} |
finally |
{ |
_databaseLock.Release(); |
} |
} |
|
#endregion |
|
#region Private Methods |
|
private static async Task SetAutoVacuum(CancellationToken cancellationToken) |
private async Task SetAutoVacuum(CancellationToken cancellationToken) |
{ |
var connectionString = new SQLiteConnectionStringBuilder |
await _databaseLock.WaitAsync(cancellationToken); |
try |
{ |
ConnectionString = DatabaseConnectionString |
}; |
|
using (var sqliteConnection = |
new SQLiteConnection(connectionString.ConnectionString)) |
new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
|
@@ -1461,15 +1793,18 @@ |
} |
} |
} |
finally |
{ |
_databaseLock.Release(); |
} |
} |
|
private static async Task CreateDatabase(CancellationToken cancellationToken) |
private async Task CreateDatabase(CancellationToken cancellationToken) |
{ |
var connectionString = new SQLiteConnectionStringBuilder |
await _databaseLock.WaitAsync(cancellationToken); |
try |
{ |
ConnectionString = DatabaseConnectionString |
}; |
|
using (var sqliteConnection = new SQLiteConnection(connectionString.ConnectionString)) |
using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) |
{ |
await sqliteConnection.OpenAsync(cancellationToken); |
|
@@ -1494,7 +1829,14 @@ |
} |
} |
} |
finally |
{ |
_databaseLock.Release(); |
} |
} |
|
#endregion |
|
|
} |
} |