Horizon

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ 1  →  ?path2? @ HEAD
/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
 
 
}
}