Merge pull request #2529 from Danstahr/custom_upgrades

Added support for executing non-SQL code when performing a database upgrade
This commit is contained in:
Kenneth Skovhede
2017-06-09 13:20:56 +02:00
committed by GitHub
4 changed files with 77 additions and 2 deletions
@@ -0,0 +1,19 @@
using System.Collections.Generic;
using Duplicati.Library.SQLiteHelper.DBUpdates;
namespace Duplicati.Library.SQLiteHelper.DBSchemaUpgrades
{
class DbUpgradesRegistry
{
/// <summary>
/// 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.
/// </summary>
public static readonly IDictionary<int, IDbSchemaUpgrade> CodeChanges =
new Dictionary<int, IDbSchemaUpgrade>()
{
};
}
}
@@ -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
{
/// <summary>
/// Executed before the textual SQL command is executed.
/// </summary>
/// <param name="connection"></param>
void BeforeSql(System.Data.IDbConnection connection);
/// <summary>
/// Executed after the textual SQL command is executed.
/// </summary>
/// <param name="connection"></param>
void AfterSql(System.Data.IDbConnection connection);
}
}
@@ -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
{
/// <summary>
/// This class will read embedded files from the given folder.
/// Updates should have the form &quot;1.Sample upgrade.sql&quot;.
/// 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;
}
@@ -38,6 +38,8 @@
<Reference Include="System.Data" />
</ItemGroup>
<ItemGroup>
<Compile Include="DBSchemaUpgrades\DbUpgradesRegistry.cs" />
<Compile Include="DBSchemaUpgrades\IDbSchemaUpgrade.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="DatabaseUpgrader.cs" />
<Compile Include="SQLiteLoader.cs" />