added support for removing columns

This commit is contained in:
kay.one 2013-07-04 20:56:27 -07:00
commit 99daa47f89
13 changed files with 331 additions and 147 deletions

View file

@ -3,24 +3,13 @@ using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
/* [Tags("")]
[Tags("")]
[Migration(7)]
public class remove_backlog : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
var newSeriesTable = "CREATE TABLE [Series_new] ([Id] integer NOT NULL PRIMARY KEY AUTOINCREMENT, [TvdbId] integer NOT NULL, " +
"[TvRageId] integer NOT NULL, [ImdbId] text NOT NULL, [Title] text NOT NULL, [TitleSlug] text NOT NULL, " +
"[CleanTitle] text NOT NULL, [Status] integer NOT NULL, [Overview] text, [AirTime] text, " +
"[Images] text NOT NULL, [Path] text NOT NULL, [Monitored] integer NOT NULL, [QualityProfileId] integer NOT NULL, " +
"[SeasonFolder] integer NOT NULL, [LastInfoSync] datetime, [LastDiskSync] datetime, [Runtime] integer NOT NULL, " +
"[SeriesType] integer NOT NULL, [Network] text, [CustomStartDate] datetime, " +
"[UseSceneNumbering] integer NOT NULL, [FirstAired] datetime)";
Execute.Sql(newSeriesTable);
Execute.Sql("INSERT INTO Series_new SELECT * FROM Series");
SQLiteAlter.DropColumns("Series", new[] { "BacklogSetting" });
}
}*/
}
}

View file

@ -0,0 +1,8 @@
namespace NzbDrone.Core.Datastore.Migration.Framework
{
public class MigrationContext
{
public MigrationType MigrationType { get; set; }
public ISQLiteAlter SQLiteAlter { get; set; }
}
}

View file

@ -14,12 +14,14 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
public class MigrationController : IMigrationController
{
private readonly IAnnouncer _announcer;
private readonly ISQLiteAlter _sqLiteAlter;
private static readonly HashSet<string> MigrationCache = new HashSet<string>();
public MigrationController(IAnnouncer announcer)
public MigrationController(IAnnouncer announcer, ISQLiteAlter sqLiteAlter)
{
_announcer = announcer;
_sqLiteAlter = sqLiteAlter;
}
public void MigrateToLatest(string connectionString, MigrationType migrationType)
@ -35,7 +37,11 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
var migrationContext = new RunnerContext(_announcer)
{
Namespace = "NzbDrone.Core.Datastore.Migration",
ApplicationContext = migrationType
ApplicationContext = new MigrationContext
{
MigrationType = migrationType,
SQLiteAlter = _sqLiteAlter
}
};
var options = new MigrationOptions { PreviewOnly = false, Timeout = 60 };

View file

@ -14,7 +14,11 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
public override void Up()
{
switch ((MigrationType)ApplicationContext)
var context = (MigrationContext)ApplicationContext;
SQLiteAlter = context.SQLiteAlter;
switch (context.MigrationType)
{
case MigrationType.Main:
MainDbUpgrade();
@ -29,6 +33,7 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
}
}
public ISQLiteAlter SQLiteAlter { get; private set; }
public override void Down()
{

View file

@ -0,0 +1,134 @@
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.Linq;
using System.Text.RegularExpressions;
namespace NzbDrone.Core.Datastore.Migration.Framework
{
public interface ISQLiteMigrationHelper
{
Dictionary<String, SQLiteMigrationHelper.SQLiteColumn> GetColumns(string tableName);
void CreateTable(string tableName, IEnumerable<SQLiteMigrationHelper.SQLiteColumn> values);
void CopyData(string sourceTable, string destinationTable, IEnumerable<SQLiteMigrationHelper.SQLiteColumn> columns);
int GetRowCount(string tableName);
void DropTable(string tableName);
void RenameTable(string tableName, string newName);
SQLiteTransaction BeginTransaction();
}
public class SQLiteMigrationHelper : ISQLiteMigrationHelper
{
private readonly SQLiteConnection _connection;
private static readonly Regex SchemaRegex = new Regex(@"['\""\[](?<name>\w+)['\""\]]\s(?<schema>[\w-\s]+)",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline);
public SQLiteMigrationHelper(IConnectionStringFactory connectionStringFactory)
{
_connection = new SQLiteConnection(connectionStringFactory.MainDbConnectionString);
_connection.Open();
}
private string GetOriginalSql(string tableName)
{
var command =
new SQLiteCommand(string.Format("SELECT sql FROM sqlite_master WHERE type='table' AND name ='{0}'",
tableName));
command.Connection = _connection;
return (string)command.ExecuteScalar();
}
public Dictionary<String, SQLiteColumn> GetColumns(string tableName)
{
var originalSql = GetOriginalSql(tableName);
var matches = SchemaRegex.Matches(originalSql);
return matches.Cast<Match>().ToDictionary(
match => match.Groups["name"].Value.Trim(),
match => new SQLiteColumn
{
Name = match.Groups["name"].Value.Trim(),
Schema = match.Groups["schema"].Value.Trim()
});
}
public void CreateTable(string tableName, IEnumerable<SQLiteColumn> values)
{
var columns = String.Join(",", values.Select(c => c.ToString()));
var command = new SQLiteCommand(string.Format("CREATE TABLE [{0}] ({1})", tableName, columns));
command.Connection = _connection;
command.ExecuteNonQuery();
}
public void CopyData(string sourceTable, string destinationTable, IEnumerable<SQLiteColumn> columns)
{
var originalCount = GetRowCount(sourceTable);
var columnsToTransfer = String.Join(",", columns.Select(c => c.Name));
var transferCommand = BuildCommand("INSERT INTO {0} SELECT {1} FROM {2};", destinationTable, columnsToTransfer, sourceTable);
transferCommand.ExecuteNonQuery();
var transferredRows = GetRowCount(destinationTable);
if (transferredRows != originalCount)
{
throw new ApplicationException(string.Format("Expected {0} rows to be copied from [{1}] to [{2}]. But only copied {3}", originalCount, sourceTable, destinationTable, transferredRows));
}
}
public void DropTable(string tableName)
{
var dropCommand = BuildCommand("DROP TABLE {0};", tableName);
dropCommand.ExecuteNonQuery();
}
public void RenameTable(string tableName, string newName)
{
var renameCommand = BuildCommand("ALTER TABLE {0} RENAME TO {1};", tableName, newName);
renameCommand.ExecuteNonQuery();
}
public int GetRowCount(string tableName)
{
var countCommand = BuildCommand("SELECT COUNT(*) FROM {0};", tableName);
return Convert.ToInt32(countCommand.ExecuteScalar());
}
public SQLiteTransaction BeginTransaction()
{
return _connection.BeginTransaction();
}
private SQLiteCommand BuildCommand(string format, params string[] args)
{
var command = new SQLiteCommand(string.Format(format, args));
command.Connection = _connection;
return command;
}
public class SQLiteColumn
{
public string Name { get; set; }
public string Schema { get; set; }
public override string ToString()
{
return string.Format("[{0}] {1}", Name, Schema);
}
}
}
}

View file

@ -1,69 +1,42 @@
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using FluentMigrator.Builders.Execute;
namespace NzbDrone.Core.Datastore.Migration.Framework
{
public class SQLiteAlter
public interface ISQLiteAlter
{
private readonly SQLiteConnection _connection;
private static readonly Regex SchemaRegex = new Regex(@"[\""\[](?<name>\w+)[\""\]]\s(?<schema>[\w-\s]+)",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline);
public SQLiteAlter(string connectionString)
{
_connection = new SQLiteConnection(connectionString);
_connection.Open();
}
private string GetOriginalSql(string tableName)
{
var command =
new SQLiteCommand(string.Format("SELECT sql FROM sqlite_master WHERE type='table' AND name ='{0}'",
tableName));
command.Connection = _connection;
return (string)command.ExecuteScalar();
}
public Dictionary<String, SQLiteColumn> GetColumns(string tableName)
{
var originalSql = GetOriginalSql(tableName);
var matches = SchemaRegex.Matches(originalSql);
return matches.Cast<Match>().ToDictionary(
match => match.Groups["name"].Value.Trim(),
match => new SQLiteColumn
{
Name = match.Groups["name"].Value.Trim(),
Schema = match.Groups["schema"].Value.Trim()
});
}
public void CreateTable(string tableName, Dictionary<string, SQLiteColumn>.ValueCollection values)
{
var columns = String.Join(",", values.Select(c => c.ToString()));
var command = new SQLiteCommand(string.Format("CREATE TABLE [{0}] ({1})", tableName, columns));
command.Connection = _connection;
command.ExecuteNonQuery();
}
void DropColumns(string tableName, IEnumerable<string> columns);
}
public class SQLiteColumn
public class SQLiteAlter : ISQLiteAlter
{
public string Name { get; set; }
public string Schema { get; set; }
private readonly ISQLiteMigrationHelper _sqLiteMigrationHelper;
public override string ToString()
public SQLiteAlter(ISQLiteMigrationHelper sqLiteMigrationHelper)
{
return string.Format("[{0}] {1}", Name, Schema);
_sqLiteMigrationHelper = sqLiteMigrationHelper;
}
public void DropColumns(string tableName, IEnumerable<string> columns)
{
using (var transaction = _sqLiteMigrationHelper.BeginTransaction())
{
var originalColumns = _sqLiteMigrationHelper.GetColumns(tableName);
var newColumns = originalColumns.Where(c => !columns.Contains(c.Key)).Select(c => c.Value).ToList();
var tempTableName = tableName + "_temp";
_sqLiteMigrationHelper.CreateTable(tempTableName, newColumns);
_sqLiteMigrationHelper.CopyData(tableName, tempTableName, newColumns);
_sqLiteMigrationHelper.DropTable(tableName);
_sqLiteMigrationHelper.RenameTable(tempTableName, tableName);
transaction.Commit();
}
}
}
}