diff --git a/Duplicati/Library/SQLiteHelper/DBSchemaUpgrades/DbUpgradesRegistry.cs b/Duplicati/Library/SQLiteHelper/DBSchemaUpgrades/DbUpgradesRegistry.cs
new file mode 100644
index 000000000..a0eecc779
--- /dev/null
+++ b/Duplicati/Library/SQLiteHelper/DBSchemaUpgrades/DbUpgradesRegistry.cs
@@ -0,0 +1,19 @@
+using System.Collections.Generic;
+using Duplicati.Library.SQLiteHelper.DBUpdates;
+
+namespace Duplicati.Library.SQLiteHelper.DBSchemaUpgrades
+{
+ class DbUpgradesRegistry
+ {
+ ///
+ /// Registry of custom code to be executed along a SQL schema upgrade. The key of the map
+ /// represents a 1-based version the upgrade code applies to (after performing the update
+ /// the database will be at that version), the value is an instance of upgrader that
+ /// implements the hooks code.
+ ///
+ public static readonly IDictionary CodeChanges =
+ new Dictionary()
+ {
+ };
+ }
+}
diff --git a/Duplicati/Library/SQLiteHelper/DBSchemaUpgrades/IDbSchemaUpgrade.cs b/Duplicati/Library/SQLiteHelper/DBSchemaUpgrades/IDbSchemaUpgrade.cs
new file mode 100644
index 000000000..4033d9242
--- /dev/null
+++ b/Duplicati/Library/SQLiteHelper/DBSchemaUpgrades/IDbSchemaUpgrade.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Duplicati.Library.SQLiteHelper.DBUpdates
+{
+
+ // TODO: Consider passing in a transaction and performing everything in one go.
+
+ public interface IDbSchemaUpgrade
+ {
+ ///
+ /// Executed before the textual SQL command is executed.
+ ///
+ ///
+ void BeforeSql(System.Data.IDbConnection connection);
+
+ ///
+ /// Executed after the textual SQL command is executed.
+ ///
+ ///
+ void AfterSql(System.Data.IDbConnection connection);
+ }
+}
diff --git a/Duplicati/Library/SQLiteHelper/DatabaseUpgrader.cs b/Duplicati/Library/SQLiteHelper/DatabaseUpgrader.cs
index 18d44788f..9bf4fab50 100644
--- a/Duplicati/Library/SQLiteHelper/DatabaseUpgrader.cs
+++ b/Duplicati/Library/SQLiteHelper/DatabaseUpgrader.cs
@@ -23,13 +23,24 @@ using System.Text;
using System.Data;
using System.Text.RegularExpressions;
using System.Linq;
+using Duplicati.Library.SQLiteHelper.DBUpdates;
+using Duplicati.Library.SQLiteHelper.DBSchemaUpgrades;
namespace Duplicati.Library.SQLiteHelper
{
///
/// This class will read embedded files from the given folder.
/// Updates should have the form "1.Sample upgrade.sql".
- /// When the database schema changes, simply put a new file into the folder.
+ /// When the database schema changes, simply put a new file into the folder
+ /// and set it to be emnbedded in the binary.
+ ///
+ /// Additionally, it's possible to execute custom code before and after
+ /// the SQL is executed. To set up a custom upgrade stage, add your
+ /// code to DbUpgradesRegistry along with the DB version to apply it with.
+ ///
+ /// Even if all the DB upgrade code is handled in C#, you still have to add
+ /// a dummy SQL file to indicate the version ID is already taken.
+ ///
/// Each upgrade file should ONLY upgrade from the previous version.
/// If done correctly, a user may be upgrade from the very first version
/// to the very latest.
@@ -235,7 +246,9 @@ namespace Duplicati.Library.SQLiteHelper
}
else if (versions.Count > dbversion)
{
- string backupfile = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(sourcefile), Strings.DatabaseUpgrader.BackupFilenamePrefix + " " + DateTime.Now.ToString("yyyyMMddhhmmss", System.Globalization.CultureInfo.InvariantCulture) + ".sqlite");
+ string backupfile = System.IO.Path.Combine(
+ System.IO.Path.GetDirectoryName(sourcefile),
+ Strings.DatabaseUpgrader.BackupFilenamePrefix + " " + DateTime.Now.ToString("yyyyMMddhhmmss", System.Globalization.CultureInfo.InvariantCulture) + ".sqlite");
try
{
@@ -247,6 +260,16 @@ namespace Duplicati.Library.SQLiteHelper
for (int i = dbversion; i < versions.Count; i++)
{
+ IDbSchemaUpgrade dbCodeUpgrade;
+
+ // The versions in the registry are 1-based, the loop index is zero based.
+ bool hookFound = DbUpgradesRegistry.CodeChanges.TryGetValue(i + 1, out dbCodeUpgrade);
+
+ if (hookFound)
+ {
+ dbCodeUpgrade.BeforeSql(connection);
+ }
+
//TODO: Find a better way to split SQL statements, as there may be embedded semicolons
//in the SQL, like "UPDATE x WHERE y = ';';"
@@ -261,6 +284,11 @@ namespace Duplicati.Library.SQLiteHelper
cmd.ExecuteNonQuery();
}
+ if (hookFound)
+ {
+ dbCodeUpgrade.AfterSql(connection);
+ }
+
// after upgrade, db_version should have changed to i + 1. If logic changes, just requery.
preparserVars["db_version"] = i + 1;
}
diff --git a/Duplicati/Library/SQLiteHelper/Duplicati.Library.SQLiteHelper.csproj b/Duplicati/Library/SQLiteHelper/Duplicati.Library.SQLiteHelper.csproj
index 9cf37e422..06d9deab3 100644
--- a/Duplicati/Library/SQLiteHelper/Duplicati.Library.SQLiteHelper.csproj
+++ b/Duplicati/Library/SQLiteHelper/Duplicati.Library.SQLiteHelper.csproj
@@ -38,6 +38,8 @@
+
+