2017-09-04 02:20:56 +00:00
using System ;
2017-10-25 01:25:29 +00:00
using System.Collections.Generic ;
2014-08-25 21:29:37 +00:00
using System.Linq ;
2019-10-28 21:12:26 +00:00
using System.Text.RegularExpressions ;
2014-08-25 21:29:37 +00:00
using FluentMigrator.Expressions ;
using FluentMigrator.Model ;
using FluentMigrator.Runner.Generators.SQLite ;
2019-10-28 21:12:26 +00:00
using FluentMigrator.Runner.Initialization ;
using FluentMigrator.Runner.Processors ;
2014-08-25 21:29:37 +00:00
using FluentMigrator.Runner.Processors.SQLite ;
2019-10-28 21:12:26 +00:00
using Microsoft.Extensions.Logging ;
using Microsoft.Extensions.Options ;
2014-08-25 21:29:37 +00:00
namespace NzbDrone.Core.Datastore.Migration.Framework
{
2019-10-28 21:12:26 +00:00
public class NzbDroneSQLiteProcessor : SQLiteProcessor
2014-08-25 21:29:37 +00:00
{
2019-10-28 21:12:26 +00:00
public NzbDroneSQLiteProcessor ( SQLiteDbFactory factory ,
SQLiteGenerator generator ,
ILogger < NzbDroneSQLiteProcessor > logger ,
IOptionsSnapshot < ProcessorOptions > options ,
IConnectionStringAccessor connectionStringAccessor ,
2022-04-14 02:08:14 +00:00
IServiceProvider serviceProvider ,
SQLiteQuoter sqliteQuoter )
: base ( factory , generator , logger , options , connectionStringAccessor , serviceProvider , sqliteQuoter )
2014-08-25 21:29:37 +00:00
{
}
public override void Process ( AlterColumnExpression expression )
{
var tableDefinition = GetTableSchema ( expression . TableName ) ;
var columnDefinitions = tableDefinition . Columns . ToList ( ) ;
var columnIndex = columnDefinitions . FindIndex ( c = > c . Name = = expression . Column . Name ) ;
if ( columnIndex = = - 1 )
{
2015-10-03 17:45:26 +00:00
throw new ApplicationException ( string . Format ( "Column {0} does not exist on table {1}." , expression . Column . Name , expression . TableName ) ) ;
2014-08-25 21:29:37 +00:00
}
columnDefinitions [ columnIndex ] = expression . Column ;
tableDefinition . Columns = columnDefinitions ;
ProcessAlterTable ( tableDefinition ) ;
}
public override void Process ( DeleteColumnExpression expression )
{
var tableDefinition = GetTableSchema ( expression . TableName ) ;
var columnDefinitions = tableDefinition . Columns . ToList ( ) ;
var indexDefinitions = tableDefinition . Indexes . ToList ( ) ;
var columnsToRemove = expression . ColumnNames . ToList ( ) ;
columnDefinitions . RemoveAll ( c = > columnsToRemove . Remove ( c . Name ) ) ;
indexDefinitions . RemoveAll ( i = > i . Columns . Any ( c = > expression . ColumnNames . Contains ( c . Name ) ) ) ;
tableDefinition . Columns = columnDefinitions ;
tableDefinition . Indexes = indexDefinitions ;
if ( columnsToRemove . Any ( ) )
{
2015-10-03 17:45:26 +00:00
throw new ApplicationException ( string . Format ( "Column {0} does not exist on table {1}." , columnsToRemove . First ( ) , expression . TableName ) ) ;
2014-08-25 21:29:37 +00:00
}
ProcessAlterTable ( tableDefinition ) ;
}
2017-10-25 01:25:29 +00:00
public override void Process ( RenameColumnExpression expression )
{
var tableDefinition = GetTableSchema ( expression . TableName ) ;
var oldColumnDefinitions = tableDefinition . Columns . ToList ( ) ;
var columnDefinitions = tableDefinition . Columns . ToList ( ) ;
var columnIndex = columnDefinitions . FindIndex ( c = > c . Name = = expression . OldName ) ;
if ( columnIndex = = - 1 )
{
throw new ApplicationException ( string . Format ( "Column {0} does not exist on table {1}." , expression . OldName , expression . TableName ) ) ;
}
2020-01-03 12:49:24 +00:00
2017-10-25 01:25:29 +00:00
if ( columnDefinitions . Any ( c = > c . Name = = expression . NewName ) )
{
throw new ApplicationException ( string . Format ( "Column {0} already exists on table {1}." , expression . NewName , expression . TableName ) ) ;
}
oldColumnDefinitions [ columnIndex ] = ( ColumnDefinition ) columnDefinitions [ columnIndex ] . Clone ( ) ;
columnDefinitions [ columnIndex ] . Name = expression . NewName ;
foreach ( var index in tableDefinition . Indexes )
{
if ( index . Name . StartsWith ( "IX_" ) )
{
index . Name = Regex . Replace ( index . Name , "(?<=_)" + Regex . Escape ( expression . OldName ) + "(?=_|$)" , Regex . Escape ( expression . NewName ) ) ;
}
foreach ( var column in index . Columns )
{
if ( column . Name = = expression . OldName )
{
column . Name = expression . NewName ;
}
}
}
ProcessAlterTable ( tableDefinition , oldColumnDefinitions ) ;
}
2015-10-03 17:45:26 +00:00
protected virtual TableDefinition GetTableSchema ( string tableName )
2014-08-25 21:29:37 +00:00
{
2019-10-28 21:12:26 +00:00
var schemaDumper = new SqliteSchemaDumper ( this ) ;
2014-08-25 21:29:37 +00:00
var schema = schemaDumper . ReadDbSchema ( ) ;
return schema . Single ( v = > v . Name = = tableName ) ;
}
2017-10-25 01:25:29 +00:00
protected virtual void ProcessAlterTable ( TableDefinition tableDefinition , List < ColumnDefinition > oldColumnDefinitions = null )
2014-08-25 21:29:37 +00:00
{
var tableName = tableDefinition . Name ;
var tempTableName = tableName + "_temp" ;
var uid = 0 ;
while ( TableExists ( null , tempTableName ) )
{
tempTableName = tableName + "_temp" + uid + + ;
}
// What is the cleanest way to do this? Add function to Generator?
2016-01-15 07:07:39 +00:00
var quoter = new SQLiteQuoter ( ) ;
2017-10-25 01:25:29 +00:00
var columnsToInsert = string . Join ( ", " , tableDefinition . Columns . Select ( c = > quoter . QuoteColumnName ( c . Name ) ) ) ;
var columnsToFetch = string . Join ( ", " , ( oldColumnDefinitions ? ? tableDefinition . Columns ) . Select ( c = > quoter . QuoteColumnName ( c . Name ) ) ) ;
2014-08-25 21:29:37 +00:00
Process ( new CreateTableExpression ( ) { TableName = tempTableName , Columns = tableDefinition . Columns . ToList ( ) } ) ;
2017-10-25 01:25:29 +00:00
Process ( string . Format ( "INSERT INTO {0} ({1}) SELECT {2} FROM {3}" , quoter . QuoteTableName ( tempTableName ) , columnsToInsert , columnsToFetch , quoter . QuoteTableName ( tableName ) ) ) ;
2014-08-25 21:29:37 +00:00
Process ( new DeleteTableExpression ( ) { TableName = tableName } ) ;
Process ( new RenameTableExpression ( ) { OldName = tempTableName , NewName = tableName } ) ;
foreach ( var index in tableDefinition . Indexes )
{
Process ( new CreateIndexExpression ( ) { Index = index } ) ;
}
}
}
}