diff --git a/NzbDrone.Core.Test/Datastore/SQLiteMigrationHelperFixture.cs b/NzbDrone.Core.Test/Datastore/SQLiteMigrationHelperTests/AlterFixture.cs similarity index 78% rename from NzbDrone.Core.Test/Datastore/SQLiteMigrationHelperFixture.cs rename to NzbDrone.Core.Test/Datastore/SQLiteMigrationHelperTests/AlterFixture.cs index 386f63bf0..7e1510259 100644 --- a/NzbDrone.Core.Test/Datastore/SQLiteMigrationHelperFixture.cs +++ b/NzbDrone.Core.Test/Datastore/SQLiteMigrationHelperTests/AlterFixture.cs @@ -7,17 +7,17 @@ using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Tv; using System.Linq; -namespace NzbDrone.Core.Test.Datastore +namespace NzbDrone.Core.Test.Datastore.SQLiteMigrationHelperTests { [TestFixture] - public class SQLiteMigrationHelperFixture : DbTest + public class AlterFixture : DbTest { - private SQLiteMigrationHelper _subject; + private SqLiteMigrationHelper _subject; [SetUp] public void SetUp() { - _subject = Mocker.Resolve(); + _subject = Mocker.Resolve(); } [Test] @@ -37,7 +37,7 @@ namespace NzbDrone.Core.Test.Datastore var columns = _subject.GetColumns("Series"); columns.Remove("Title"); - _subject.CreateTable("Series_New", columns.Values, new List()); + _subject.CreateTable("Series_New", columns.Values, new List()); var newColumns = _subject.GetColumns("Series_New"); @@ -106,5 +106,22 @@ namespace NzbDrone.Core.Test.Datastore newIndexes.Should().HaveSameCount(indexes); newIndexes.Select(c=>c.Column).Should().BeEquivalentTo(indexes.Select(c=>c.Column)); } + + + [Test] + public void should_be_able_to_create_table_with_new_indexes() + { + var columns = _subject.GetColumns("Series"); + columns.Remove("Title"); + + _subject.CreateTable("Series_New", columns.Values, new List{new SQLiteIndex{Column = "AirTime", Table = "Series_New", Unique = true}}); + + var newColumns = _subject.GetColumns("Series_New"); + var newIndexes = _subject.GetIndexes("Series_New"); + + newColumns.Values.Should().HaveSameCount(columns.Values); + newIndexes.Should().Contain(i=>i.Column == "AirTime"); + } + } } \ No newline at end of file diff --git a/NzbDrone.Core.Test/Datastore/SQLiteMigrationHelperTests/DuplicateFixture.cs b/NzbDrone.Core.Test/Datastore/SQLiteMigrationHelperTests/DuplicateFixture.cs new file mode 100644 index 000000000..67aefd265 --- /dev/null +++ b/NzbDrone.Core.Test/Datastore/SQLiteMigrationHelperTests/DuplicateFixture.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Linq; +using FizzWare.NBuilder; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Datastore.Migration.Framework; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Core.Test.Datastore.SQLiteMigrationHelperTests +{ + [TestFixture] + public class DuplicateFixture : DbTest + { + private SqLiteMigrationHelper _subject; + + [SetUp] + public void SetUp() + { + _subject = Mocker.Resolve(); + } + + + [Test] + public void get_duplicates() + { + var series = Builder.CreateListOfSize(10) + .Random(3) + .With(c => c.QualityProfileId = 100) + .BuildListOfNew(); + + Db.InsertMany(series); + + var duplicates = _subject.GetDuplicates("series", "QualityProfileId").ToList(); + + + duplicates.Should().HaveCount(1); + duplicates.First().Should().HaveCount(3); + } + + } +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/Framework/DbTest.cs b/NzbDrone.Core.Test/Framework/DbTest.cs index ec94f7418..561283875 100644 --- a/NzbDrone.Core.Test/Framework/DbTest.cs +++ b/NzbDrone.Core.Test/Framework/DbTest.cs @@ -93,7 +93,7 @@ namespace NzbDrone.Core.Test.Framework Mocker.SetConstant(Mocker.Resolve()); Mocker.SetConstant(Mocker.Resolve()); - Mocker.SetConstant(Mocker.Resolve()); + Mocker.SetConstant(Mocker.Resolve()); Mocker.SetConstant(Mocker.Resolve()); Mocker.SetConstant(Mocker.Resolve()); diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 524d84b1d..40131b5d9 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -108,7 +108,8 @@ - + + diff --git a/NzbDrone.Core/Datastore/Migration/008_remove_backlog.cs b/NzbDrone.Core/Datastore/Migration/008_remove_backlog.cs index 6614e2c14..83fd62478 100644 --- a/NzbDrone.Core/Datastore/Migration/008_remove_backlog.cs +++ b/NzbDrone.Core/Datastore/Migration/008_remove_backlog.cs @@ -8,8 +8,8 @@ namespace NzbDrone.Core.Datastore.Migration { protected override void MainDbUpgrade() { - SQLiteAlter.DropColumns("Series", new[] { "BacklogSetting" }); - SQLiteAlter.DropColumns("NamingConfig", new[] { "UseSceneName" }); + SqLiteAlter.DropColumns("Series", new[] { "BacklogSetting" }); + SqLiteAlter.DropColumns("NamingConfig", new[] { "UseSceneName" }); } } } diff --git a/NzbDrone.Core/Datastore/Migration/009_fix_renameEpisodes.cs b/NzbDrone.Core/Datastore/Migration/009_fix_renameEpisodes.cs index d52799a03..f5ef5b2d1 100644 --- a/NzbDrone.Core/Datastore/Migration/009_fix_renameEpisodes.cs +++ b/NzbDrone.Core/Datastore/Migration/009_fix_renameEpisodes.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Core.Datastore.Migration { protected override void MainDbUpgrade() { - SQLiteAlter.DropColumns("NamingConfig", new[] { "SeasonFolderFormat" }); + SqLiteAlter.DropColumns("NamingConfig", new[] { "SeasonFolderFormat" }); Execute.Sql("UPDATE NamingConfig SET RenameEpisodes = 1 WHERE RenameEpisodes = -1"); Execute.Sql("UPDATE NamingConfig SET RenameEpisodes = 0 WHERE RenameEpisodes = -2"); diff --git a/NzbDrone.Core/Datastore/Migration/011_remove_ignored.cs b/NzbDrone.Core/Datastore/Migration/011_remove_ignored.cs index 103f0648a..8f62c3b46 100644 --- a/NzbDrone.Core/Datastore/Migration/011_remove_ignored.cs +++ b/NzbDrone.Core/Datastore/Migration/011_remove_ignored.cs @@ -8,8 +8,8 @@ namespace NzbDrone.Core.Datastore.Migration { protected override void MainDbUpgrade() { - SQLiteAlter.DropColumns("Episodes", new[] { "Ignored" }); - SQLiteAlter.DropColumns("Seasons", new[] { "Ignored" }); + SqLiteAlter.DropColumns("Episodes", new[] { "Ignored" }); + SqLiteAlter.DropColumns("Seasons", new[] { "Ignored" }); } } } diff --git a/NzbDrone.Core/Datastore/Migration/012_remove_custom_start_date.cs b/NzbDrone.Core/Datastore/Migration/012_remove_custom_start_date.cs index 417e73013..a7f156c87 100644 --- a/NzbDrone.Core/Datastore/Migration/012_remove_custom_start_date.cs +++ b/NzbDrone.Core/Datastore/Migration/012_remove_custom_start_date.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Core.Datastore.Migration { protected override void MainDbUpgrade() { - SQLiteAlter.DropColumns("Series", new[] { "CustomStartDate" }); + SqLiteAlter.DropColumns("Series", new[] { "CustomStartDate" }); } } } diff --git a/NzbDrone.Core/Datastore/Migration/014_drop_air_date.cs b/NzbDrone.Core/Datastore/Migration/014_drop_air_date.cs index 434ccc8a2..b88282202 100644 --- a/NzbDrone.Core/Datastore/Migration/014_drop_air_date.cs +++ b/NzbDrone.Core/Datastore/Migration/014_drop_air_date.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Core.Datastore.Migration { protected override void MainDbUpgrade() { - SQLiteAlter.DropColumns("Episodes", new []{ "AirDate" }); + SqLiteAlter.DropColumns("Episodes", new []{ "AirDate" }); } } } diff --git a/NzbDrone.Core/Datastore/Migration/018_remove_duplicates.cs b/NzbDrone.Core/Datastore/Migration/018_remove_duplicates.cs new file mode 100644 index 000000000..379f79a43 --- /dev/null +++ b/NzbDrone.Core/Datastore/Migration/018_remove_duplicates.cs @@ -0,0 +1,60 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; +using System.Linq; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(18)] + public class remove_duplicates : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + using (var transaction = MigrationHelper.BeginTransaction()) + { + RemoveDuplicateSeries("TvdbId"); + RemoveDuplicateSeries("TitleSlug"); + + var duplicatedEpisodes = MigrationHelper.GetDuplicates("Episodes", "TvDbEpisodeId"); + + foreach (var duplicate in duplicatedEpisodes) + { + foreach (var episodeId in duplicate.OrderBy(c => c.Key).Skip(1).Select(c => c.Key)) + { + RemoveEpisodeRows(episodeId); + } + } + + transaction.Commit(); + } + } + + private void RemoveDuplicateSeries(string field) + { + var duplicatedSeries = MigrationHelper.GetDuplicates("Series", field); + + foreach (var duplicate in duplicatedSeries) + { + foreach (var seriesId in duplicate.OrderBy(c => c.Key).Skip(1).Select(c => c.Key)) + { + RemoveSeriesRows(seriesId); + } + } + } + + private void RemoveSeriesRows(int seriesId) + { + MigrationHelper.ExecuteNonQuery("DELETE FROM Series WHERE Id = {0}", seriesId.ToString()); + MigrationHelper.ExecuteNonQuery("DELETE FROM Episodes WHERE SeriesId = {0}", seriesId.ToString()); + MigrationHelper.ExecuteNonQuery("DELETE FROM Seasons WHERE SeriesId = {0}", seriesId.ToString()); + MigrationHelper.ExecuteNonQuery("DELETE FROM History WHERE SeriesId = {0}", seriesId.ToString()); + MigrationHelper.ExecuteNonQuery("DELETE FROM EpisodeFiles WHERE SeriesId = {0}", seriesId.ToString()); + } + + private void RemoveEpisodeRows(int episodeId) + { + MigrationHelper.ExecuteNonQuery("DELETE FROM Episodes WHERE Id = {0}", episodeId.ToString()); + MigrationHelper.ExecuteNonQuery("DELETE FROM History WHERE EpisodeId = {0}", episodeId.ToString()); + } + + } +} diff --git a/NzbDrone.Core/Datastore/Migration/019_restore_unique_constraints.cs b/NzbDrone.Core/Datastore/Migration/019_restore_unique_constraints.cs new file mode 100644 index 000000000..c4e304df7 --- /dev/null +++ b/NzbDrone.Core/Datastore/Migration/019_restore_unique_constraints.cs @@ -0,0 +1,20 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(19)] + public class restore_unique_constraints : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + SqLiteAlter.AddIndexes("Series", + new SQLiteIndex { Column = "TvdbId", Table = "Series", Unique = true }, + new SQLiteIndex { Column = "TitleSlug", Table = "Series", Unique = true }); + + SqLiteAlter.AddIndexes("Episodes", + new SQLiteIndex { Column = "TvDbEpisodeId", Table = "Episodes", Unique = true }); + } + + } +} diff --git a/NzbDrone.Core/Datastore/Migration/Framework/MigrationContext.cs b/NzbDrone.Core/Datastore/Migration/Framework/MigrationContext.cs index ebf634c15..90ac77a1e 100644 --- a/NzbDrone.Core/Datastore/Migration/Framework/MigrationContext.cs +++ b/NzbDrone.Core/Datastore/Migration/Framework/MigrationContext.cs @@ -4,5 +4,6 @@ { public MigrationType MigrationType { get; set; } public ISQLiteAlter SQLiteAlter { get; set; } + public ISqLiteMigrationHelper MigrationHelper { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Core/Datastore/Migration/Framework/MigrationController.cs b/NzbDrone.Core/Datastore/Migration/Framework/MigrationController.cs index e2a0480f2..a930511c3 100644 --- a/NzbDrone.Core/Datastore/Migration/Framework/MigrationController.cs +++ b/NzbDrone.Core/Datastore/Migration/Framework/MigrationController.cs @@ -15,13 +15,15 @@ namespace NzbDrone.Core.Datastore.Migration.Framework { private readonly IAnnouncer _announcer; private readonly ISQLiteAlter _sqLiteAlter; + private readonly ISqLiteMigrationHelper _migrationHelper; private static readonly HashSet MigrationCache = new HashSet(); - public MigrationController(IAnnouncer announcer, ISQLiteAlter sqLiteAlter) + public MigrationController(IAnnouncer announcer, ISQLiteAlter sqLiteAlter, ISqLiteMigrationHelper migrationHelper) { _announcer = announcer; _sqLiteAlter = sqLiteAlter; + _migrationHelper = migrationHelper; } public void MigrateToLatest(string connectionString, MigrationType migrationType) @@ -40,7 +42,8 @@ namespace NzbDrone.Core.Datastore.Migration.Framework ApplicationContext = new MigrationContext { MigrationType = migrationType, - SQLiteAlter = _sqLiteAlter + SQLiteAlter = _sqLiteAlter, + MigrationHelper = _migrationHelper, } }; diff --git a/NzbDrone.Core/Datastore/Migration/Framework/NzbDroneMigrationBase.cs b/NzbDrone.Core/Datastore/Migration/Framework/NzbDroneMigrationBase.cs index a1be291fe..da9dde57a 100644 --- a/NzbDrone.Core/Datastore/Migration/Framework/NzbDroneMigrationBase.cs +++ b/NzbDrone.Core/Datastore/Migration/Framework/NzbDroneMigrationBase.cs @@ -1,9 +1,18 @@ using System; +using NLog; +using NzbDrone.Common.Instrumentation; namespace NzbDrone.Core.Datastore.Migration.Framework { public abstract class NzbDroneMigrationBase : FluentMigrator.Migration { + private Logger _logger; + + protected NzbDroneMigrationBase() + { + _logger = NzbDroneLogger.GetLogger(); + } + protected virtual void MainDbUpgrade() { } @@ -16,7 +25,8 @@ namespace NzbDrone.Core.Datastore.Migration.Framework { var context = (MigrationContext)ApplicationContext; - SQLiteAlter = context.SQLiteAlter; + SqLiteAlter = context.SQLiteAlter; + MigrationHelper = context.MigrationHelper; switch (context.MigrationType) { @@ -33,7 +43,8 @@ namespace NzbDrone.Core.Datastore.Migration.Framework } } - protected ISQLiteAlter SQLiteAlter { get; private set; } + protected ISQLiteAlter SqLiteAlter { get; private set; } + protected ISqLiteMigrationHelper MigrationHelper { get; private set; } public override void Down() { diff --git a/NzbDrone.Core/Datastore/Migration/Framework/SQLiteColumn.cs b/NzbDrone.Core/Datastore/Migration/Framework/SQLiteColumn.cs new file mode 100644 index 000000000..976916885 --- /dev/null +++ b/NzbDrone.Core/Datastore/Migration/Framework/SQLiteColumn.cs @@ -0,0 +1,13 @@ +namespace NzbDrone.Core.Datastore.Migration.Framework +{ + public class SQLiteColumn + { + public string Name { get; set; } + public string Schema { get; set; } + + public override string ToString() + { + return string.Format("[{0}] {1}", Name, Schema); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Datastore/Migration/Framework/SQLiteIndex.cs b/NzbDrone.Core/Datastore/Migration/Framework/SQLiteIndex.cs new file mode 100644 index 000000000..cbf18dbd9 --- /dev/null +++ b/NzbDrone.Core/Datastore/Migration/Framework/SQLiteIndex.cs @@ -0,0 +1,39 @@ +using System; + +namespace NzbDrone.Core.Datastore.Migration.Framework +{ + public class SQLiteIndex : IEquatable + { + public string Column { get; set; } + public string Table { get; set; } + public bool Unique { get; set; } + + public bool Equals(SQLiteIndex other) + { + return IndexName == other.IndexName; + } + + public override int GetHashCode() + { + return IndexName.GetHashCode(); + } + + public override string ToString() + { + return string.Format("[{0}] Unique: {1}", Column, Unique); + } + + public string IndexName + { + get + { + return string.Format("IX_{0}_{1}", Table, Column); + } + } + + public string CreateSql(string tableName) + { + return string.Format(@"CREATE UNIQUE INDEX ""{2}"" ON ""{0}"" (""{1}"" ASC)", tableName, Column, IndexName); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Datastore/Migration/Framework/SQLiteMigrationHelper.cs b/NzbDrone.Core/Datastore/Migration/Framework/SQLiteMigrationHelper.cs index 3813c0e79..843b97466 100644 --- a/NzbDrone.Core/Datastore/Migration/Framework/SQLiteMigrationHelper.cs +++ b/NzbDrone.Core/Datastore/Migration/Framework/SQLiteMigrationHelper.cs @@ -7,18 +7,21 @@ using NLog; namespace NzbDrone.Core.Datastore.Migration.Framework { - public interface ISQLiteMigrationHelper + public interface ISqLiteMigrationHelper { - Dictionary GetColumns(string tableName); - void CreateTable(string tableName, IEnumerable values, IEnumerable indexes); - void CopyData(string sourceTable, string destinationTable, IEnumerable columns); + Dictionary GetColumns(string tableName); + void CreateTable(string tableName, IEnumerable values, IEnumerable indexes); + void CopyData(string sourceTable, string destinationTable, IEnumerable columns); void DropTable(string tableName); void RenameTable(string tableName, string newName); + IEnumerable>> GetDuplicates(string tableName, string columnName); SQLiteTransaction BeginTransaction(); - List GetIndexes(string tableName); + List GetIndexes(string tableName); + int ExecuteScalar(string command, params string[] args); + void ExecuteNonQuery(string command, params string[] args); } - public class SQLiteMigrationHelper : ISQLiteMigrationHelper + public class SqLiteMigrationHelper : ISqLiteMigrationHelper { private readonly SQLiteConnection _connection; @@ -28,7 +31,7 @@ namespace NzbDrone.Core.Datastore.Migration.Framework private static readonly Regex IndexRegex = new Regex(@"\(""(?.*)""\s(?ASC|DESC)\)$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline); - public SQLiteMigrationHelper(IConnectionStringFactory connectionStringFactory, Logger logger) + public SqLiteMigrationHelper(IConnectionStringFactory connectionStringFactory, Logger logger) { try { @@ -69,18 +72,22 @@ namespace NzbDrone.Core.Datastore.Migration.Framework }); } + + private static IEnumerable ReadArray(SQLiteDataReader reader) + { + while (reader.Read()) + { + yield return (T)Convert.ChangeType(reader[0], typeof(T)); + } + } + public List GetIndexes(string tableName) { var command = new SQLiteCommand(string.Format("SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name ='{0}'", tableName)); command.Connection = _connection; var reader = command.ExecuteReader(); - var sqls = new List(); - - while (reader.Read()) - { - sqls.Add(reader[0].ToString()); - } + var sqls = ReadArray(reader).ToList(); var indexes = new List(); @@ -108,7 +115,7 @@ namespace NzbDrone.Core.Datastore.Migration.Framework foreach (var index in indexes) { - ExecuteNonQuery("DROP INDEX {0}", index.IndexName); + ExecuteNonQuery("DROP INDEX IF EXISTS {0}", index.IndexName); ExecuteNonQuery(index.CreateSql(tableName)); } } @@ -146,6 +153,23 @@ namespace NzbDrone.Core.Datastore.Migration.Framework renameCommand.ExecuteNonQuery(); } + public IEnumerable>> GetDuplicates(string tableName, string columnName) + { + var getDuplicates = BuildCommand("select id, {0} from {1}", columnName, tableName); + + var result = new List>(); + + using (var reader = getDuplicates.ExecuteReader()) + { + while (reader.Read()) + { + result.Add(new KeyValuePair(reader.GetInt16(0), (T)Convert.ChangeType(reader[1], typeof(T)))); + } + } + + return result.GroupBy(c => c.Value).Where(g => g.Count() > 1); + } + public int GetRowCount(string tableName) { var countCommand = BuildCommand("SELECT COUNT(*) FROM {0};", tableName); @@ -166,8 +190,7 @@ namespace NzbDrone.Core.Datastore.Migration.Framework } - - private void ExecuteNonQuery(string command, params string[] args) + public void ExecuteNonQuery(string command, params string[] args) { var sqLiteCommand = new SQLiteCommand(string.Format(command, args)) { @@ -177,43 +200,17 @@ namespace NzbDrone.Core.Datastore.Migration.Framework sqLiteCommand.ExecuteNonQuery(); } - - public class SQLiteColumn + public int ExecuteScalar(string command, params string[] args) { - public string Name { get; set; } - public string Schema { get; set; } - - public override string ToString() + var sqLiteCommand = new SQLiteCommand(string.Format(command, args)) { - return string.Format("[{0}] {1}", Name, Schema); - } + Connection = _connection + }; + + return (int)sqLiteCommand.ExecuteScalar(); } - public class SQLiteIndex - { - public string Column { get; set; } - public string Table { get; set; } - public bool Unique { get; set; } - public override string ToString() - { - return string.Format("[{0}] Unique: {1}", Column, Unique); - } - - public string IndexName - { - get - { - return string.Format("IX_{0}_{1}", Table, Column); - } - } - - public string CreateSql(string tableName) - { - return string.Format(@"CREATE UNIQUE INDEX ""{2}"" ON ""{0}"" (""{1}"" ASC)", tableName, Column, IndexName); - } - } + } - - } \ No newline at end of file diff --git a/NzbDrone.Core/Datastore/Migration/Framework/SqliteAlter.cs b/NzbDrone.Core/Datastore/Migration/Framework/SqliteAlter.cs index 572b0c493..c22670588 100644 --- a/NzbDrone.Core/Datastore/Migration/Framework/SqliteAlter.cs +++ b/NzbDrone.Core/Datastore/Migration/Framework/SqliteAlter.cs @@ -6,13 +6,14 @@ namespace NzbDrone.Core.Datastore.Migration.Framework public interface ISQLiteAlter { void DropColumns(string tableName, IEnumerable columns); + void AddIndexes(string tableName, params SQLiteIndex[] indexes); } public class SQLiteAlter : ISQLiteAlter { - private readonly ISQLiteMigrationHelper _sqLiteMigrationHelper; + private readonly ISqLiteMigrationHelper _sqLiteMigrationHelper; - public SQLiteAlter(ISQLiteMigrationHelper sqLiteMigrationHelper) + public SQLiteAlter(ISqLiteMigrationHelper sqLiteMigrationHelper) { _sqLiteMigrationHelper = sqLiteMigrationHelper; } @@ -27,18 +28,39 @@ namespace NzbDrone.Core.Datastore.Migration.Framework var newColumns = originalColumns.Where(c => !columns.Contains(c.Key)).Select(c => c.Value).ToList(); var newIndexes = originalIndexes.Where(c => !columns.Contains(c.Column)); - var tempTableName = tableName + "_temp"; - - _sqLiteMigrationHelper.CreateTable(tempTableName, newColumns, newIndexes); - - _sqLiteMigrationHelper.CopyData(tableName, tempTableName, newColumns); - - _sqLiteMigrationHelper.DropTable(tableName); - - _sqLiteMigrationHelper.RenameTable(tempTableName, tableName); + CreateTable(tableName, newColumns, newIndexes); transaction.Commit(); } } + + public void AddIndexes(string tableName, params SQLiteIndex[] indexes) + { + using (var transaction = _sqLiteMigrationHelper.BeginTransaction()) + { + var columns = _sqLiteMigrationHelper.GetColumns(tableName).Select(c => c.Value).ToList(); + var originalIndexes = _sqLiteMigrationHelper.GetIndexes(tableName); + + var newIndexes = originalIndexes.Union(indexes); + + + CreateTable(tableName, columns, newIndexes); + + transaction.Commit(); + } + } + + private void CreateTable(string tableName, List newColumns, IEnumerable newIndexes) + { + var tempTableName = tableName + "_temp"; + + _sqLiteMigrationHelper.CreateTable(tempTableName, newColumns, newIndexes); + + _sqLiteMigrationHelper.CopyData(tableName, tempTableName, newColumns); + + _sqLiteMigrationHelper.DropTable(tableName); + + _sqLiteMigrationHelper.RenameTable(tempTableName, tableName); + } } } \ No newline at end of file diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index 2f39373e1..d15de6be0 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -160,6 +160,8 @@ + + @@ -169,6 +171,8 @@ + +