restore sqlite indexes on alter.

This commit is contained in:
kay.one 2013-09-01 12:07:48 -07:00
parent 4196b7eedb
commit 2068b732a2
3 changed files with 120 additions and 21 deletions

View File

@ -1,4 +1,5 @@
using FizzWare.NBuilder; using System.Collections.Generic;
using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Datastore.Migration.Framework; using NzbDrone.Core.Datastore.Migration.Framework;
@ -18,8 +19,6 @@ namespace NzbDrone.Core.Test.Datastore
_subject = Mocker.Resolve<SQLiteMigrationHelper>(); _subject = Mocker.Resolve<SQLiteMigrationHelper>();
} }
[Test] [Test]
public void should_parse_existing_columns() public void should_parse_existing_columns()
{ {
@ -37,7 +36,7 @@ namespace NzbDrone.Core.Test.Datastore
var columns = _subject.GetColumns("Series"); var columns = _subject.GetColumns("Series");
columns.Remove("Title"); columns.Remove("Title");
_subject.CreateTable("Series_New", columns.Values); _subject.CreateTable("Series_New", columns.Values, new List<SQLiteMigrationHelper.SQLiteIndex>());
var newColumns = _subject.GetColumns("Series_New"); var newColumns = _subject.GetColumns("Series_New");
@ -45,12 +44,6 @@ namespace NzbDrone.Core.Test.Datastore
newColumns.Should().NotContainKey("Title"); newColumns.Should().NotContainKey("Title");
} }
[Test]
public void should_get_zero_count_on_empty_table()
{
_subject.GetRowCount("Series").Should().Be(0);
}
[Test] [Test]
public void should_be_able_to_transfer_empty_tables() public void should_be_able_to_transfer_empty_tables()
@ -58,7 +51,7 @@ namespace NzbDrone.Core.Test.Datastore
var columns = _subject.GetColumns("Series"); var columns = _subject.GetColumns("Series");
columns.Remove("Title"); columns.Remove("Title");
_subject.CreateTable("Series_New", columns.Values); _subject.CreateTable("Series_New", columns.Values, new List<SQLiteMigrationHelper.SQLiteIndex>());
_subject.CopyData("Series", "Series_New", columns.Values); _subject.CopyData("Series", "Series_New", columns.Values);
@ -74,11 +67,40 @@ namespace NzbDrone.Core.Test.Datastore
var columns = _subject.GetColumns("Episodes"); var columns = _subject.GetColumns("Episodes");
columns.Remove("Title"); columns.Remove("Title");
_subject.CreateTable("Episodes_New", columns.Values); _subject.CreateTable("Episodes_New", columns.Values, new List<SQLiteMigrationHelper.SQLiteIndex>());
_subject.CopyData("Episodes", "Episodes_New", columns.Values); _subject.CopyData("Episodes", "Episodes_New", columns.Values);
_subject.GetRowCount("Episodes_New").Should().Be(originalEpisodes.Count); _subject.GetRowCount("Episodes_New").Should().Be(originalEpisodes.Count);
} }
[Test]
public void should_read_existing_indexes()
{
var indexes = _subject.GetIndexes("QualitySizes");
indexes.Should().NotBeEmpty();
indexes.Should().OnlyContain(c => c != null);
indexes.Should().OnlyContain(c => !string.IsNullOrWhiteSpace(c.Column));
indexes.Should().OnlyContain(c => c.Table == "QualitySizes");
indexes.Should().OnlyContain(c => c.Unique);
}
[Test]
public void should_add_indexes_when_creating_new_table()
{
var columns = _subject.GetColumns("QualitySizes");
var indexes = _subject.GetIndexes("QualitySizes");
_subject.CreateTable("QualityB", columns.Values, indexes);
var newIndexes = _subject.GetIndexes("QualityB");
newIndexes.Should().HaveSameCount(indexes);
newIndexes.Should().BeEquivalentTo(columns);
}
} }
} }

View File

@ -10,12 +10,12 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
public interface ISQLiteMigrationHelper public interface ISQLiteMigrationHelper
{ {
Dictionary<String, SQLiteMigrationHelper.SQLiteColumn> GetColumns(string tableName); Dictionary<String, SQLiteMigrationHelper.SQLiteColumn> GetColumns(string tableName);
void CreateTable(string tableName, IEnumerable<SQLiteMigrationHelper.SQLiteColumn> values); void CreateTable(string tableName, IEnumerable<SQLiteMigrationHelper.SQLiteColumn> values, IEnumerable<SQLiteMigrationHelper.SQLiteIndex> indexes);
void CopyData(string sourceTable, string destinationTable, IEnumerable<SQLiteMigrationHelper.SQLiteColumn> columns); void CopyData(string sourceTable, string destinationTable, IEnumerable<SQLiteMigrationHelper.SQLiteColumn> columns);
int GetRowCount(string tableName);
void DropTable(string tableName); void DropTable(string tableName);
void RenameTable(string tableName, string newName); void RenameTable(string tableName, string newName);
SQLiteTransaction BeginTransaction(); SQLiteTransaction BeginTransaction();
List<SQLiteMigrationHelper.SQLiteIndex> GetIndexes(string tableName);
} }
public class SQLiteMigrationHelper : ISQLiteMigrationHelper public class SQLiteMigrationHelper : ISQLiteMigrationHelper
@ -25,7 +25,10 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
private static readonly Regex SchemaRegex = new Regex(@"['\""\[](?<name>\w+)['\""\]]\s(?<schema>[\w-\s]+)", private static readonly Regex SchemaRegex = new Regex(@"['\""\[](?<name>\w+)['\""\]]\s(?<schema>[\w-\s]+)",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline); RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline);
public SQLiteMigrationHelper(IConnectionStringFactory connectionStringFactory,Logger logger) private static readonly Regex IndexRegex = new Regex(@"\(""(?<col>.*)""\s(?<direction>ASC|DESC)\)$",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline);
public SQLiteMigrationHelper(IConnectionStringFactory connectionStringFactory, Logger logger)
{ {
try try
{ {
@ -34,7 +37,7 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
} }
catch (Exception e) catch (Exception e)
{ {
logger.ErrorException("Couldn't open databse " + connectionStringFactory.MainDbConnectionString, e); logger.ErrorException("Couldn't open database " + connectionStringFactory.MainDbConnectionString, e);
throw; throw;
} }
@ -47,6 +50,7 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
tableName)); tableName));
command.Connection = _connection; command.Connection = _connection;
return (string)command.ExecuteScalar(); return (string)command.ExecuteScalar();
} }
@ -65,14 +69,48 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
}); });
} }
public void CreateTable(string tableName, IEnumerable<SQLiteColumn> values) public List<SQLiteIndex> 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<string>();
while (reader.Read())
{
sqls.Add(reader[0].ToString());
}
var indexes = new List<SQLiteIndex>();
foreach (var indexSql in sqls)
{
var newIndex = new SQLiteIndex();
var matches = IndexRegex.Match(indexSql);
newIndex.Column = matches.Groups["col"].Value;
newIndex.Unique = indexSql.Contains("UNIQUE");
newIndex.Table = tableName;
indexes.Add(newIndex);
}
return indexes;
}
public void CreateTable(string tableName, IEnumerable<SQLiteColumn> values, IEnumerable<SQLiteIndex> indexes)
{ {
var columns = String.Join(",", values.Select(c => c.ToString())); var columns = String.Join(",", values.Select(c => c.ToString()));
var command = new SQLiteCommand(string.Format("CREATE TABLE [{0}] ({1})", tableName, columns)); ExecuteNonQuery("CREATE TABLE [{0}] ({1})", tableName, columns);
command.Connection = _connection;
command.ExecuteNonQuery(); foreach (var index in indexes)
{
ExecuteNonQuery("DROP INDEX {0}", index.IndexName);
ExecuteNonQuery(index.CreateSql(tableName));
}
} }
public void CopyData(string sourceTable, string destinationTable, IEnumerable<SQLiteColumn> columns) public void CopyData(string sourceTable, string destinationTable, IEnumerable<SQLiteColumn> columns)
@ -128,6 +166,18 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
} }
private void ExecuteNonQuery(string command, params string[] args)
{
var sqLiteCommand = new SQLiteCommand(string.Format(command, args))
{
Connection = _connection
};
sqLiteCommand.ExecuteNonQuery();
}
public class SQLiteColumn public class SQLiteColumn
{ {
public string Name { get; set; } public string Name { get; set; }
@ -138,6 +188,31 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
return string.Format("[{0}] {1}", Name, Schema); return string.Format("[{0}] {1}", Name, Schema);
} }
} }
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);
}
}
} }

View File

@ -22,12 +22,14 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
using (var transaction = _sqLiteMigrationHelper.BeginTransaction()) using (var transaction = _sqLiteMigrationHelper.BeginTransaction())
{ {
var originalColumns = _sqLiteMigrationHelper.GetColumns(tableName); var originalColumns = _sqLiteMigrationHelper.GetColumns(tableName);
var originalIndexes = _sqLiteMigrationHelper.GetIndexes(tableName);
var newColumns = originalColumns.Where(c => !columns.Contains(c.Key)).Select(c => c.Value).ToList(); 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"; var tempTableName = tableName + "_temp";
_sqLiteMigrationHelper.CreateTable(tempTableName, newColumns); _sqLiteMigrationHelper.CreateTable(tempTableName, newColumns, newIndexes);
_sqLiteMigrationHelper.CopyData(tableName, tempTableName, newColumns); _sqLiteMigrationHelper.CopyData(tableName, tempTableName, newColumns);