From 0d30307fcea1edc9725fb95b7e2183172202f817 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Thu, 24 Jun 2010 16:05:08 -0700 Subject: [PATCH] Data migration schema API --HG-- branch : dev --- .../DataMigrationCommandsTests.cs | 38 --- .../DataMigration/DataMigrationTests.cs | 38 ++- .../DataMigration/SchemaBuilderTests.cs | 135 ++++++++ .../Utilities/NullInterpreter.cs | 27 ++ .../Orchard.Framework.Tests.csproj | 3 +- .../DataMigration/DataMigrationManager.cs | 8 +- .../DefaultDataMigrationInterpreter.cs | 293 ++++++++++++++++++ .../Interpreters/ICommandInterpreter.cs | 15 + .../Interpreters/IDataMigrationInterpreter.cs | 13 + .../Interpreters/SqLiteCommandInterpreter.cs | 34 ++ .../DataMigration/Schema/AddColumnCommand.cs | 8 + .../Schema/AlterColumnCommand.cs | 28 +- .../DataMigration/Schema/AlterTableCommand.cs | 49 +-- .../DataMigration/Schema/ColumnCommand.cs | 40 ++- .../Schema/CreateColumnCommand.cs | 68 ++-- .../Schema/CreateForeignKeyCommand.cs | 34 +- .../Schema/CreateIndexCommand.cs | 4 +- .../Schema/CreateTableCommand.cs | 17 +- .../DataMigration/Schema/DropColumnCommand.cs | 6 +- .../Schema/DropForeignKeyCommand.cs | 8 +- .../DataMigration/Schema/DropTableCommand.cs | 2 +- .../Schema/ISchemaBuilderCommand.cs | 9 + .../DataMigration/Schema/IShellSettings.cs | 1 + .../DataMigration/Schema/SchemaBuilder.cs | 45 ++- .../DataMigration/Schema/SchemaCommand.cs | 30 +- .../Schema/SqlStatementCommand.cs | 11 +- .../DataMigration/Schema/TableCommand.cs | 8 +- src/Orchard/Orchard.Framework.csproj | 7 + 28 files changed, 775 insertions(+), 204 deletions(-) delete mode 100644 src/Orchard.Tests/DataMigration/DataMigrationCommandsTests.cs create mode 100644 src/Orchard.Tests/DataMigration/SchemaBuilderTests.cs create mode 100644 src/Orchard.Tests/DataMigration/Utilities/NullInterpreter.cs create mode 100644 src/Orchard/DataMigration/Interpreters/DefaultDataMigrationInterpreter.cs create mode 100644 src/Orchard/DataMigration/Interpreters/ICommandInterpreter.cs create mode 100644 src/Orchard/DataMigration/Interpreters/IDataMigrationInterpreter.cs create mode 100644 src/Orchard/DataMigration/Interpreters/SqLiteCommandInterpreter.cs create mode 100644 src/Orchard/DataMigration/Schema/AddColumnCommand.cs create mode 100644 src/Orchard/DataMigration/Schema/ISchemaBuilderCommand.cs create mode 100644 src/Orchard/DataMigration/Schema/IShellSettings.cs diff --git a/src/Orchard.Tests/DataMigration/DataMigrationCommandsTests.cs b/src/Orchard.Tests/DataMigration/DataMigrationCommandsTests.cs deleted file mode 100644 index 6d9fc665c..000000000 --- a/src/Orchard.Tests/DataMigration/DataMigrationCommandsTests.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Data; -using NUnit.Framework; -using Orchard.DataMigration.Schema; - -namespace Orchard.Tests.DataMigration { - [TestFixture] - public class DataMigrationCommandsTests { - - [Test] - public void AllMethodsShouldBeCalledSuccessfully() { - var schemaBuilder = new SchemaBuilder("TEST_"); - - schemaBuilder - .CreateTable("User", table => table - .ContentPartRecord() - .Column("Id", DbType.Int32, column => column.PrimaryKey()) - .Column("Firstname", DbType.String, column => column.Length(255)) - .Column("Lastname", DbType.String, column => column.Precision(0).Scale(1)) - .ForeignKey("User_Address", fk => fk.On("Id", "Address", "UserId"))) - .CreateTable("Address", table => table - .VersionedContentPartRecord() - .Column("City", DbType.String) - .Column("ZIP", DbType.Int32, column => column.Unique()) - .Column("UserId", DbType.Int32, column => column.NotNull())) - .AlterTable("User", table => table - .AddColumn("Age", DbType.Int32) - .AlterColumn("Lastname", column => column.Default("John")) - .AlterColumn("Lastname", column => column.Rename("John")) - .DropColumn("Lastname") - .CreateIndex("IDX_XYZ", "NickName") - .DropIndex("IDX_XYZ") - .AddForeignKey("FKL", fk => fk.On("Id", "A", "Id").On("Id", "B", "Id") ) - .DropForeignKey("FKL")) - .DropTable("Address") - .ExecuteSql("DROP DATABASE", statement => statement.ForDialect("SQLite").ForDialect("MsSqlServer2008")); - } - } -} diff --git a/src/Orchard.Tests/DataMigration/DataMigrationTests.cs b/src/Orchard.Tests/DataMigration/DataMigrationTests.cs index 26c19c624..f151226ca 100644 --- a/src/Orchard.Tests/DataMigration/DataMigrationTests.cs +++ b/src/Orchard.Tests/DataMigration/DataMigrationTests.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; +using System.Data; using System.Linq; using Autofac; using NHibernate; using NUnit.Framework; using Orchard.ContentManagement.Records; using Orchard.Data; +using Orchard.DataMigration.Interpreters; using Orchard.Environment.Configuration; using Orchard.Environment.Extensions; using Orchard.Environment.Extensions.Folders; @@ -49,6 +51,7 @@ namespace Orchard.Tests.DataMigration { builder.RegisterInstance(new ShellSettings { DataTablePrefix = "TEST_"}); + builder.RegisterType().As(); builder.RegisterInstance(_folders).As(); builder.RegisterType().As(); builder.RegisterType().As(); @@ -135,7 +138,6 @@ namespace Orchard.Tests.DataMigration { public int UpdateFrom666() { return 999; } - } public class DataMigrationDependenciesModule1 : IDataMigration { @@ -165,7 +167,6 @@ namespace Orchard.Tests.DataMigration { public int Create() { Assert.That(SchemaBuilder, Is.Not.Null); - Assert.That(SchemaBuilder.TablePrefix, Is.EqualTo("TEST_")); return 1; } } @@ -176,7 +177,6 @@ namespace Orchard.Tests.DataMigration { } } - public class DataMigrationFeatureNeedUpdate2 : IDataMigration { public string Feature { get { return "Feature2"; } @@ -200,6 +200,20 @@ namespace Orchard.Tests.DataMigration { return 999; } } + + public class DataMigrationSimpleBuilder : DataMigrationImpl { + public override string Feature { + get { return "Feature1"; } + } + + public int Create() { + SchemaBuilder.CreateTable("UserRecord", table => + table.Column("Id", DbType.Int32, column => + column.PrimaryKey())); + + return 1; + } + } [Test] public void DataMigrationShouldDoNothingIfNoDataMigrationIsProvidedForFeature() { @@ -389,5 +403,23 @@ features: Assert.That(_dataMigrationManager.GetFeaturesThatNeedUpdate().Contains("Feature3"), Is.False); } + + + [Test] public void SchemaBuilderShouldCreateSql() { + + Init(new[] { typeof(DataMigrationSimpleBuilder) }); + + _folders.Manifests.Add("Module1", @" +name: Module1 +version: 0.1 +orchardversion: 1 +features: + Feature1: + Description: Feature +"); + + _dataMigrationManager.Update("Feature1"); + } + } } \ No newline at end of file diff --git a/src/Orchard.Tests/DataMigration/SchemaBuilderTests.cs b/src/Orchard.Tests/DataMigration/SchemaBuilderTests.cs new file mode 100644 index 000000000..870b02421 --- /dev/null +++ b/src/Orchard.Tests/DataMigration/SchemaBuilderTests.cs @@ -0,0 +1,135 @@ +using System.Data; +using System.Linq; +using Autofac; +using NHibernate; +using NUnit.Framework; +using Orchard.Data; +using Orchard.DataMigration.Interpreters; +using Orchard.DataMigration.Schema; +using Orchard.Environment.Configuration; +using Orchard.Tests.ContentManagement; +using System.IO; + +namespace Orchard.Tests.DataMigration { + [TestFixture] + public class SchemaBuilderTests { + private IContainer _container; + private ISessionFactory _sessionFactory; + private string _databaseFileName; + private SchemaBuilder _schemaBuilder; + private DefaultDataMigrationInterpreter _interpreter; + + [SetUp] + public void Setup() { + _databaseFileName = Path.GetTempFileName(); + _sessionFactory = DataUtility.CreateSessionFactory( + _databaseFileName); + + var builder = new ContainerBuilder(); + + builder.RegisterInstance(new ShellSettings { DataTablePrefix = "TEST_", DataProvider = "SQLite" }); + + var session = _sessionFactory.OpenSession(); + builder.RegisterType().As(); + builder.RegisterInstance(new DefaultContentManagerTests.TestSessionLocator(session)).As(); + builder.RegisterInstance(new ShellSettings { DataProvider = "SQLite", DataTablePrefix = "TEST_" }).As(); + builder.RegisterType().As(); + _container = builder.Build(); + + _interpreter = _container.Resolve() as DefaultDataMigrationInterpreter; + _schemaBuilder = new SchemaBuilder(_interpreter); + } + + [Test] + public void AllMethodsShouldBeCalledSuccessfully() { + + _schemaBuilder = new SchemaBuilder(new NullInterpreter()); + + _schemaBuilder + .CreateTable("User", table => table + .ContentPartRecord() + .Column("Id", DbType.Int32, column => column.PrimaryKey()) + .Column("Firstname", DbType.String, column => column.WithLength(255)) + .Column("Lastname", DbType.String, column => column.WithPrecision(0).WithScale(1))) + .CreateTable("Address", table => table + .VersionedContentPartRecord() + .Column("City", DbType.String) + .Column("ZIP", DbType.Int32, column => column.Unique()) + .Column("UserId", DbType.Int32, column => column.NotNull())) + .CreateForeignKey("User_Address", "User", new[] { "UserId" }, "User", new[] { "Id" }) + .AlterTable("User", table => table + .AddColumn("Age", DbType.Int32)) + .AlterTable("User", table => table + .DropColumn("Lastname")) + .AlterTable("User", table => table + .CreateIndex("IDX_XYZ", "NickName")) + .AlterTable("User", table => table + .DropIndex("IDX_XYZ")) + .DropForeignKey("Addresse", "User_Address") + .DropTable("Address") + .ExecuteSql("drop database", statement => statement.ForProvider("SQLite")) + .ExecuteSql("DROP DATABASE", statement => statement.ForProvider("SQLServer")); + } + + [Test] + public void CreateCommandShouldBeHandled() { + + _schemaBuilder + .CreateTable("User", table => table + .Column("Id", DbType.Int32, column => column.PrimaryKey()) + .Column("Firstname", DbType.String, column => column.WithLength(255)) + .Column("Lastname", DbType.String, column => column.WithLength(100).NotNull()) + .Column("SN", DbType.AnsiString, column => column.WithLength(40).Unique()) + .Column("Salary", DbType.Decimal, column => column.WithPrecision(9).WithScale(2)) + .Column("Gender", DbType.Decimal, column => column.WithDefault("''")) + ); + } + + [Test] + public void DropTableCommandShouldBeHandled() { + + _schemaBuilder + .DropTable("User"); + } + + [Test] + public void CustomSqlStatementsShouldBeHandled() { + + _schemaBuilder + .ExecuteSql("select 1"); + } + + [Test] + public void AlterTableCommandShouldBeHandled() { + + _schemaBuilder + .CreateTable("User", table => table + .Column("Firstname", DbType.String, column => column.WithLength(255)) + .Column("Lastname", DbType.String, column => column.WithLength(100).NotNull())) + .AlterTable("User", table => table + .AddColumn("Age", DbType.Int32)) + .AlterTable("User", table => table + .AlterColumn("Lastname", column => column.WithDefault("'John'"))) + .AlterTable("User", table => table + .DropColumn("Firstname") + ); + } + + [Test] + public void ForeignKeyShouldBeCreatedAndRemoved() { + + _schemaBuilder + .CreateTable("User", table => table + .Column("Id", DbType.Int32, column => column.PrimaryKey()) + .Column("Firstname", DbType.String, column => column.WithLength(255)) + .Column("Lastname", DbType.String, column => column.WithPrecision(0).WithScale(1))) + .CreateTable("Address", table => table + .Column("City", DbType.String) + .Column("ZIP", DbType.Int32, column => column.Unique()) + .Column("UserId", DbType.Int32, column => column.NotNull())) + .CreateForeignKey("User_Address", "User", new[] { "UserId" }, "User", new[] { "Id" }) + .DropForeignKey("User", "User_Address"); + + } + } +} diff --git a/src/Orchard.Tests/DataMigration/Utilities/NullInterpreter.cs b/src/Orchard.Tests/DataMigration/Utilities/NullInterpreter.cs new file mode 100644 index 000000000..46cadd57d --- /dev/null +++ b/src/Orchard.Tests/DataMigration/Utilities/NullInterpreter.cs @@ -0,0 +1,27 @@ + +using System; +using Orchard.DataMigration.Interpreters; +using Orchard.DataMigration.Schema; + +public class NullInterpreter : IDataMigrationInterpreter { + public void Visit(SchemaCommand command) { + } + + public void Visit(CreateTableCommand command) { + } + + public void Visit(DropTableCommand command) { + } + + public void Visit(AlterTableCommand command) { + } + + public void Visit(SqlStatementCommand command) { + } + + public void Visit(CreateForeignKeyCommand command) { + } + + public void Visit(DropForeignKeyCommand command) { + } +} \ No newline at end of file diff --git a/src/Orchard.Tests/Orchard.Framework.Tests.csproj b/src/Orchard.Tests/Orchard.Framework.Tests.csproj index 89ab0f5a7..84df4f6de 100644 --- a/src/Orchard.Tests/Orchard.Framework.Tests.csproj +++ b/src/Orchard.Tests/Orchard.Framework.Tests.csproj @@ -180,8 +180,9 @@ Code - + + diff --git a/src/Orchard/DataMigration/DataMigrationManager.cs b/src/Orchard/DataMigration/DataMigrationManager.cs index 8d1a66e0c..32852b0b7 100644 --- a/src/Orchard/DataMigration/DataMigrationManager.cs +++ b/src/Orchard/DataMigration/DataMigrationManager.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using System.Text.RegularExpressions; using Orchard.Data; +using Orchard.DataMigration.Interpreters; using Orchard.DataMigration.Schema; using Orchard.Environment.Configuration; using Orchard.Environment.Extensions; @@ -20,18 +21,21 @@ namespace Orchard.DataMigration { private readonly IDataMigrationGenerator _dataMigrationGenerator; private readonly IExtensionManager _extensionManager; private readonly ShellSettings _shellSettings; + private readonly IDataMigrationInterpreter _interpreter; public DataMigrationManager( IEnumerable dataMigrations, IRepository dataMigrationRepository, IDataMigrationGenerator dataMigrationGenerator, IExtensionManager extensionManager, - ShellSettings shellSettings) { + ShellSettings shellSettings, + IDataMigrationInterpreter interpreter) { _dataMigrations = dataMigrations; _dataMigrationRepository = dataMigrationRepository; _dataMigrationGenerator = dataMigrationGenerator; _extensionManager = extensionManager; _shellSettings = shellSettings; + _interpreter = interpreter; Logger = NullLogger.Instance; } @@ -163,7 +167,7 @@ namespace Orchard.DataMigration { .ToList(); foreach (var migration in migrations.OfType()) { - migration.SchemaBuilder = new SchemaBuilder(_shellSettings.DataTablePrefix); + migration.SchemaBuilder = new SchemaBuilder(_interpreter); } return migrations; diff --git a/src/Orchard/DataMigration/Interpreters/DefaultDataMigrationInterpreter.cs b/src/Orchard/DataMigration/Interpreters/DefaultDataMigrationInterpreter.cs new file mode 100644 index 000000000..0966faffc --- /dev/null +++ b/src/Orchard/DataMigration/Interpreters/DefaultDataMigrationInterpreter.cs @@ -0,0 +1,293 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using NHibernate; +using NHibernate.Dialect; +using NHibernate.SqlTypes; +using Orchard.Data; +using Orchard.DataMigration.Schema; +using Orchard.Environment.Configuration; +using Orchard.Logging; + +namespace Orchard.DataMigration.Interpreters { + public class DefaultDataMigrationInterpreter : IDataMigrationInterpreter { + private readonly ShellSettings _shellSettings; + private readonly IEnumerable _commandInterpreters; + private readonly ISession _session; + private readonly Dialect _dialect; + private readonly List _sqlStatements; + private const char Space = ' ' ; + + public DefaultDataMigrationInterpreter(ShellSettings shellSettings, ISessionLocator sessionLocator, IEnumerable commandInterpreters) { + _shellSettings = shellSettings; + _commandInterpreters = commandInterpreters; + _session = sessionLocator.For(typeof(DefaultDataMigrationInterpreter)); + _sqlStatements = new List(); + _dialect = _shellSettings.DataProvider == "SQLite" ? (Dialect) new SQLiteDialect() : new MsSql2008Dialect(); + Logger = NullLogger.Instance; + } + + public ILogger Logger { get; set; } + + public IEnumerable SqlStatements { + get { return _sqlStatements; } + } + + public void Visit(SchemaCommand command) { + switch (command.Type) { + case SchemaCommandType.CreateTable: + Visit((CreateTableCommand)command); + break; + case SchemaCommandType.AlterTable: + Visit((AlterTableCommand)command); + break; + case SchemaCommandType.DropTable: + Visit((DropTableCommand)command); + break; + case SchemaCommandType.SqlStatement: + Visit((SqlStatementCommand)command); + break; + case SchemaCommandType.CreateForeignKey: + Visit((CreateForeignKeyCommand)command); + break; + case SchemaCommandType.DropForeignKey: + Visit((DropForeignKeyCommand)command); + break; + } + } + + public void Visit(CreateTableCommand command) { + + if ( ExecuteCustomInterpreter(command) ) { + return; + } + + var builder = new StringBuilder(); + + builder.Append(_dialect.CreateMultisetTableString) + .Append(' ') + .Append(_dialect.QuoteForTableName(_shellSettings.DataTablePrefix + command.Name)) + .Append(" ("); + + var appendComma = false; + foreach(var createColumn in command.TableCommands.OfType()) { + if(appendComma) { + builder.Append(", "); + } + appendComma = true; + + Visit(builder, createColumn); + } + + builder.Append(" )"); + _sqlStatements.Add(builder.ToString()); + + RunPendingStatements(); + } + + public void Visit(DropTableCommand command) { + if ( ExecuteCustomInterpreter(command) ) { + return; + } + + var builder = new StringBuilder(); + + builder.Append(_dialect.GetDropTableString(_shellSettings.DataTablePrefix + command.Name)); + _sqlStatements.Add(builder.ToString()); + + RunPendingStatements(); + } + + public void Visit(AlterTableCommand command) { + if ( ExecuteCustomInterpreter(command) ) { + return; + } + + if(command.TableCommands.Count == 0) { + return; + } + + // drop columns + foreach ( var dropColumn in command.TableCommands.OfType() ) { + var builder = new StringBuilder(); + Visit(builder, dropColumn); + RunPendingStatements(); + } + + // add columns + foreach ( var addColumn in command.TableCommands.OfType() ) { + var builder = new StringBuilder(); + Visit(builder, addColumn); + RunPendingStatements(); + } + + // alter columns + foreach ( var alterColumn in command.TableCommands.OfType() ) { + var builder = new StringBuilder(); + Visit(builder, alterColumn); + RunPendingStatements(); + } + } + + public void Visit(StringBuilder builder, AddColumnCommand command) { + if ( ExecuteCustomInterpreter(command) ) { + return; + } + + builder.AppendFormat("alter table {0} add column ", _dialect.QuoteForTableName(_shellSettings.DataTablePrefix + command.TableName)); + + Visit(builder, (CreateColumnCommand)command); + _sqlStatements.Add(builder.ToString()); + } + + public void Visit(StringBuilder builder, DropColumnCommand command) { + if ( ExecuteCustomInterpreter(command) ) { + return; + } + + builder.AppendFormat("alter table {0} drop column {1}", + _dialect.QuoteForTableName(_shellSettings.DataTablePrefix + command.TableName), + _dialect.QuoteForColumnName(command.TableName)); + _sqlStatements.Add(builder.ToString()); + } + + public void Visit(StringBuilder builder, AlterColumnCommand command) { + if ( ExecuteCustomInterpreter(command) ) { + return; + } + + builder.AppendFormat("alter table {0} alter column {1} ", + _dialect.QuoteForTableName(_shellSettings.DataTablePrefix + command.TableName), + _dialect.QuoteForColumnName(command.TableName)); + + // type + if ( command.DbType != DbType.Object ) { + builder.Append(GetTypeName(command.DbType, command.Length, command.Precision, command.Scale)); + } + + // [default value] + if ( !string.IsNullOrEmpty(command.Default) ) { + builder.Append(" default ").Append(command.Default).Append(Space); + } + _sqlStatements.Add(builder.ToString()); + } + + public void Visit(SqlStatementCommand command) { + if (command.Providers.Count == 0 || command.Providers.Contains(_shellSettings.DataProvider) ) { + if (ExecuteCustomInterpreter(command)) { + return; + } + _sqlStatements.Add(command.Sql); + + RunPendingStatements(); + } + } + + public void Visit(CreateForeignKeyCommand command) { + if ( ExecuteCustomInterpreter(command) ) { + return; + } + + var builder = new StringBuilder(); + + builder.Append("alter table ") + .Append(_dialect.QuoteForTableName(_shellSettings.DataTablePrefix + command.SrcTable)); + + builder.Append(_dialect.GetAddForeignKeyConstraintString(command.Name, + command.SrcColumns, + command.DestTable, + command.DestColumns, + false)); + + _sqlStatements.Add(builder.ToString()); + + RunPendingStatements(); + } + + public void Visit(DropForeignKeyCommand command) { + if ( ExecuteCustomInterpreter(command) ) { + return; + } + + var builder = new StringBuilder(); + + builder.AppendFormat("alter table {0} drop constraint {1}", command.SrcTable, command.Name); + _sqlStatements.Add(builder.ToString()); + + RunPendingStatements(); + } + + private string GetTypeName(DbType dbType, int? length, byte precision, byte scale) { + return precision > 0 + ? _dialect.GetTypeName(new SqlType(dbType, precision, scale)) + : length.HasValue + ? _dialect.GetTypeName(new SqlType(dbType, length.Value)) + : _dialect.GetTypeName(new SqlType(dbType)); + } + + private void Visit(StringBuilder builder, CreateColumnCommand command) { + if ( ExecuteCustomInterpreter(command) ) { + return; + } + + // name + builder.Append(_dialect.QuoteForColumnName(command.ColumnName)).Append(Space); + + // type + builder.Append(GetTypeName(command.DbType, command.Length, command.Precision, command.Scale)); + + // [default value] + if ( !string.IsNullOrEmpty(command.Default) ) { + builder.Append(" default ").Append(command.Default).Append(Space); + } + + // nullable + builder.Append(command.IsNotNull + ? " not null" + : !command.IsPrimaryKey && !command.IsUnique + ? _dialect.NullColumnString + : string.Empty); + + // append unique if handled, otherwise at the end of the satement + if ( command.IsUnique && _dialect.SupportsUnique ) { + builder.Append(" unique"); + } + + if ( command.IsPrimaryKey ) { + builder.Append(Space).Append(_dialect.PrimaryKeyString); + } + } + + private void RunPendingStatements() { + + var connection = _session.Connection; + + foreach ( var sqlStatement in _sqlStatements ) { + Logger.Debug(sqlStatement); + using ( var command = connection.CreateCommand() ) { + command.CommandText = sqlStatement; + command.ExecuteNonQuery(); + } + } + + _sqlStatements.Clear(); + } + + private bool ExecuteCustomInterpreter(T command) where T : ISchemaBuilderCommand { + var interpreter = _commandInterpreters + .Where(ici => ici.DataProvider == _shellSettings.DataProvider) + .OfType>() + .FirstOrDefault(); + + if ( interpreter != null ) { + _sqlStatements.AddRange(interpreter.CreateStatements(command)); + RunPendingStatements(); + return true; + } + + return false; + } + } +} diff --git a/src/Orchard/DataMigration/Interpreters/ICommandInterpreter.cs b/src/Orchard/DataMigration/Interpreters/ICommandInterpreter.cs new file mode 100644 index 000000000..389f3e685 --- /dev/null +++ b/src/Orchard/DataMigration/Interpreters/ICommandInterpreter.cs @@ -0,0 +1,15 @@ +using Orchard.DataMigration.Schema; + +namespace Orchard.DataMigration.Interpreters { + /// + /// This interface can be implemented to provide a data migration behavior + /// + public interface ICommandInterpreter : ICommandInterpreter + where T : ISchemaBuilderCommand { + string[] CreateStatements(T command); + } + + public interface ICommandInterpreter : IDependency { + string DataProvider { get; } + } +} diff --git a/src/Orchard/DataMigration/Interpreters/IDataMigrationInterpreter.cs b/src/Orchard/DataMigration/Interpreters/IDataMigrationInterpreter.cs new file mode 100644 index 000000000..63fe47135 --- /dev/null +++ b/src/Orchard/DataMigration/Interpreters/IDataMigrationInterpreter.cs @@ -0,0 +1,13 @@ +using Orchard.DataMigration.Schema; + +namespace Orchard.DataMigration.Interpreters { + public interface IDataMigrationInterpreter : IDependency{ + void Visit(SchemaCommand command); + void Visit(CreateTableCommand command); + void Visit(DropTableCommand command); + void Visit(AlterTableCommand command); + void Visit(SqlStatementCommand command); + void Visit(CreateForeignKeyCommand command); + void Visit(DropForeignKeyCommand command); + } +} diff --git a/src/Orchard/DataMigration/Interpreters/SqLiteCommandInterpreter.cs b/src/Orchard/DataMigration/Interpreters/SqLiteCommandInterpreter.cs new file mode 100644 index 000000000..7dea6e6af --- /dev/null +++ b/src/Orchard/DataMigration/Interpreters/SqLiteCommandInterpreter.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Orchard.DataMigration.Schema; + +namespace Orchard.DataMigration.Interpreters { + public class SqLiteCommandInterpreter : + ICommandInterpreter, + ICommandInterpreter, + ICommandInterpreter, + ICommandInterpreter { + + public string[] CreateStatements(DropColumnCommand command) { + return new string[0]; + } + + public string[] CreateStatements(AlterColumnCommand command) { + return new string[0]; + } + + public string[] CreateStatements(CreateForeignKeyCommand command) { + return new string[0]; + } + + public string[] CreateStatements(DropForeignKeyCommand command) { + return new string[0]; + } + + public string DataProvider { + get { return "SQLite"; } + } + } +} diff --git a/src/Orchard/DataMigration/Schema/AddColumnCommand.cs b/src/Orchard/DataMigration/Schema/AddColumnCommand.cs new file mode 100644 index 000000000..1c33261f7 --- /dev/null +++ b/src/Orchard/DataMigration/Schema/AddColumnCommand.cs @@ -0,0 +1,8 @@ +using System.Data; + +namespace Orchard.DataMigration.Schema { + public class AddColumnCommand : CreateColumnCommand { + public AddColumnCommand(string tableName, string name) : base(tableName, name) { + } + } +} diff --git a/src/Orchard/DataMigration/Schema/AlterColumnCommand.cs b/src/Orchard/DataMigration/Schema/AlterColumnCommand.cs index c3d88884a..937019b67 100644 --- a/src/Orchard/DataMigration/Schema/AlterColumnCommand.cs +++ b/src/Orchard/DataMigration/Schema/AlterColumnCommand.cs @@ -1,15 +1,27 @@ -namespace Orchard.DataMigration.Schema { - public class AlterColumnCommand : ColumnCommand { - private string _newName; +using System.Data; - public AlterColumnCommand(string name) - : base(name) { +namespace Orchard.DataMigration.Schema { + public class AlterColumnCommand : ColumnCommand { + public AlterColumnCommand(string tableName, string columnName) + : base(tableName, columnName) { } - - public AlterColumnCommand Rename(string name) { - _newName = name; + public new AlterColumnCommand WithType(DbType dbType) { + base.WithType(dbType); return this; } + + public AlterColumnCommand WithType(DbType dbType, int? length) { + base.WithType(dbType).WithLength(length); + return this; + } + + public AlterColumnCommand WithType(DbType dbType, byte precision, byte scale) { + base.WithType(dbType); + Precision = precision; + Scale = scale; + return this; + } + } } diff --git a/src/Orchard/DataMigration/Schema/AlterTableCommand.cs b/src/Orchard/DataMigration/Schema/AlterTableCommand.cs index 2fee79722..02cd81cff 100644 --- a/src/Orchard/DataMigration/Schema/AlterTableCommand.cs +++ b/src/Orchard/DataMigration/Schema/AlterTableCommand.cs @@ -1,64 +1,47 @@ using System; using System.Data; +using JetBrains.Annotations; namespace Orchard.DataMigration.Schema { public class AlterTableCommand : SchemaCommand { public AlterTableCommand(string name) - : base(name) { + : base(name, SchemaCommandType.AlterTable) { } - public AlterTableCommand AddColumn(string name, DbType dbType, Action column = null) { - var command = new CreateColumnCommand(name); - command.Type(dbType); + public void AddColumn(string columnName, DbType dbType, Action column = null) { + var command = new AddColumnCommand(Name, columnName); + command.WithType(dbType); if(column != null) { column(command); } - _tableCommands.Add(command); - return this; + TableCommands.Add(command); } - public AlterTableCommand DropColumn(string name) { - var command = new DropColumnCommand(name); - _tableCommands.Add(command); - return this; + public void DropColumn(string columnName) { + var command = new DropColumnCommand(Name, columnName); + TableCommands.Add(command); } - public AlterTableCommand AlterColumn(string name, Action column = null) { - var command = new AlterColumnCommand(name); + public void AlterColumn(string columnName, Action column = null) { + var command = new AlterColumnCommand(Name, columnName); if ( column != null ) { column(command); } - _tableCommands.Add(command); - return this; + TableCommands.Add(command); } - public AlterTableCommand CreateIndex(string name, params string[] columnNames) { + public void CreateIndex(string name, params string[] columnNames) { var command = new CreateIndexCommand(name, columnNames); - _tableCommands.Add(command); - return this; + TableCommands.Add(command); } - public AlterTableCommand DropIndex(string name) { + public void DropIndex(string name) { var command = new DropIndexCommand(name); - _tableCommands.Add(command); - return this; - } - - public AlterTableCommand AddForeignKey(string name, Action fk) { - var command = new CreateForeignKeyCommand(name); - fk(command); - _tableCommands.Add(command); - return this; - } - - public AlterTableCommand DropForeignKey(string name) { - var command = new DropForeignKeyCommand(name); - _tableCommands.Add(command); - return this; + TableCommands.Add(command); } } } diff --git a/src/Orchard/DataMigration/Schema/ColumnCommand.cs b/src/Orchard/DataMigration/Schema/ColumnCommand.cs index 46e9f32a2..4777805eb 100644 --- a/src/Orchard/DataMigration/Schema/ColumnCommand.cs +++ b/src/Orchard/DataMigration/Schema/ColumnCommand.cs @@ -2,22 +2,40 @@ namespace Orchard.DataMigration.Schema { public class ColumnCommand : TableCommand { - private DbType _dbType; - private object _default; - public ColumnCommand(string name) : base(name) { - _dbType = DbType.Object; - _default = null; - } + public string ColumnName { get; set; } - public ColumnCommand Type(DbType dbType) { - _dbType = dbType; + public ColumnCommand(string tableName, string name) + : base(tableName) { + ColumnName = name; + DbType = DbType.Object; + Default = null; + Length = null; + } + public byte Scale { get; protected set; } + + public byte Precision { get; protected set; } + + public DbType DbType { get; private set; } + + public string Default { get; private set; } + + public int? Length { get; private set; } + + public ColumnCommand WithType(DbType dbType) { + DbType = dbType; return this; } - public ColumnCommand Default(object @default) { - _default = @default; + public ColumnCommand WithDefault(string @default) { + Default = @default; return this; } - } + + public ColumnCommand WithLength(int? length) { + Length = length; + return this; + } + + } } diff --git a/src/Orchard/DataMigration/Schema/CreateColumnCommand.cs b/src/Orchard/DataMigration/Schema/CreateColumnCommand.cs index 86bc1aef6..6a5dc5848 100644 --- a/src/Orchard/DataMigration/Schema/CreateColumnCommand.cs +++ b/src/Orchard/DataMigration/Schema/CreateColumnCommand.cs @@ -1,59 +1,67 @@ -namespace Orchard.DataMigration.Schema { - public class CreateColumnCommand : ColumnCommand { - private bool _primaryKey; - private byte? _precision; - private byte? _scale; - private int? _length; - private bool _notNull; - private bool _unique; - +using System.Data; - public CreateColumnCommand(string name) : base(name) { - _precision = null; - _scale = null; - _length = null; - _notNull = false; - _unique = false; - +namespace Orchard.DataMigration.Schema { + public class CreateColumnCommand : ColumnCommand { + public CreateColumnCommand(string tableName, string name) : base(tableName, name) { + IsNotNull = false; + IsUnique = false; } + public bool IsUnique { get; protected set; } + + public bool IsNotNull { get; protected set; } + + public bool IsPrimaryKey { get; protected set; } + public CreateColumnCommand PrimaryKey() { - _primaryKey = true; + IsPrimaryKey = true; + IsUnique = false; return this; } - public CreateColumnCommand Precision(byte? precision) { - _precision = precision; + public CreateColumnCommand WithPrecision(byte precision) { + Precision = precision; return this; } - public CreateColumnCommand Scale(byte? scale) { - _scale = scale; - return this; - } - - public CreateColumnCommand Length(int? length) { - _length = length; + public CreateColumnCommand WithScale(byte scale) { + Scale = scale; return this; } public CreateColumnCommand NotNull() { - _notNull = true; + IsNotNull = true; return this; } public CreateColumnCommand Nullable() { - _notNull = false; + IsNotNull = false; return this; } public CreateColumnCommand Unique() { - _unique = true; + IsUnique = true; + IsPrimaryKey = false; return this; } public CreateColumnCommand NotUnique() { - _unique = false; + IsUnique = false; + return this; + } + + public new CreateColumnCommand WithLength(int? length) { + base.WithLength(length); + return this; + } + + public new CreateColumnCommand WithType(DbType dbType) { + base.WithType(dbType); + return this; + } + + public new CreateColumnCommand WithDefault(string @default) { + base.WithDefault(@default); return this; } } diff --git a/src/Orchard/DataMigration/Schema/CreateForeignKeyCommand.cs b/src/Orchard/DataMigration/Schema/CreateForeignKeyCommand.cs index 0fe485ee5..864879a22 100644 --- a/src/Orchard/DataMigration/Schema/CreateForeignKeyCommand.cs +++ b/src/Orchard/DataMigration/Schema/CreateForeignKeyCommand.cs @@ -1,31 +1,21 @@ using System.Collections.Generic; namespace Orchard.DataMigration.Schema { - public class CreateForeignKeyCommand : TableCommand { - protected readonly List _foreignKeyClauses; + public class CreateForeignKeyCommand : SchemaCommand { - public CreateForeignKeyCommand(string name) - : base(name) { - _foreignKeyClauses = new List(); - } - - public CreateForeignKeyCommand On(string srcColumn, string destTable, string destColumn) { - _foreignKeyClauses.Add(new ForeignKeyClause(srcColumn, destTable, destColumn)); - return this; - } - } - - public class ForeignKeyClause { - public ForeignKeyClause(string srcColumn, string destTable, string destColumn) { - SrcColumn = srcColumn; - DestTable = destTable; - DestColumn = destColumn; - } - - public string DestColumn { get; private set; } + public string[] DestColumns { get; private set; } public string DestTable { get; private set; } - public string SrcColumn { get; private set; } + public string[] SrcColumns { get; private set; } + + public string SrcTable { get; private set; } + + public CreateForeignKeyCommand(string name, string srcTable, string[] srcColumns, string destTable, string[] destColumns) : base(name, SchemaCommandType.CreateForeignKey) { + SrcColumns = srcColumns; + DestTable = destTable; + DestColumns = destColumns; + SrcTable = srcTable; + } } } diff --git a/src/Orchard/DataMigration/Schema/CreateIndexCommand.cs b/src/Orchard/DataMigration/Schema/CreateIndexCommand.cs index 3460c8364..8cbb9032e 100644 --- a/src/Orchard/DataMigration/Schema/CreateIndexCommand.cs +++ b/src/Orchard/DataMigration/Schema/CreateIndexCommand.cs @@ -1,7 +1,7 @@ namespace Orchard.DataMigration.Schema { public class CreateIndexCommand : TableCommand { - public CreateIndexCommand(string name, params string[] columnNames) - : base(name) { + public CreateIndexCommand(string indexName, params string[] columnNames) + : base(indexName) { ColumnNames = columnNames; } diff --git a/src/Orchard/DataMigration/Schema/CreateTableCommand.cs b/src/Orchard/DataMigration/Schema/CreateTableCommand.cs index f49342afa..f6a22ebec 100644 --- a/src/Orchard/DataMigration/Schema/CreateTableCommand.cs +++ b/src/Orchard/DataMigration/Schema/CreateTableCommand.cs @@ -4,17 +4,17 @@ using System.Data; namespace Orchard.DataMigration.Schema { public class CreateTableCommand : SchemaCommand { public CreateTableCommand(string name) - : base(name) { + : base(name, SchemaCommandType.CreateTable) { } - public CreateTableCommand Column(string name, DbType dbType, Action column = null) { - var command = new CreateColumnCommand(name); - command.Type(dbType); + public CreateTableCommand Column(string columnName, DbType dbType, Action column = null) { + var command = new CreateColumnCommand(Name, columnName); + command.WithType(dbType); if ( column != null ) { column(command); } - _tableCommands.Add(command); + TableCommands.Add(command); return this; } @@ -27,12 +27,5 @@ namespace Orchard.DataMigration.Schema { /// TODO: Call Column() with necessary information for content part records return this; } - - public CreateTableCommand ForeignKey(string name, Action fk) { - var command = new CreateForeignKeyCommand(name); - fk(command); - _tableCommands.Add(command); - return this; - } } } diff --git a/src/Orchard/DataMigration/Schema/DropColumnCommand.cs b/src/Orchard/DataMigration/Schema/DropColumnCommand.cs index 03e32b942..84739b096 100644 --- a/src/Orchard/DataMigration/Schema/DropColumnCommand.cs +++ b/src/Orchard/DataMigration/Schema/DropColumnCommand.cs @@ -1,8 +1,8 @@ namespace Orchard.DataMigration.Schema { public class DropColumnCommand : ColumnCommand { - public DropColumnCommand(string name) - : base(name) { - + + public DropColumnCommand(string tableName, string columnName) + : base(tableName, columnName) { } } } diff --git a/src/Orchard/DataMigration/Schema/DropForeignKeyCommand.cs b/src/Orchard/DataMigration/Schema/DropForeignKeyCommand.cs index ceb1fe530..ac7367cba 100644 --- a/src/Orchard/DataMigration/Schema/DropForeignKeyCommand.cs +++ b/src/Orchard/DataMigration/Schema/DropForeignKeyCommand.cs @@ -1,8 +1,10 @@ namespace Orchard.DataMigration.Schema { - public class DropForeignKeyCommand : TableCommand { + public class DropForeignKeyCommand : SchemaCommand { + public string SrcTable { get; private set; } - public DropForeignKeyCommand(string name) - : base(name) { + public DropForeignKeyCommand(string srcTable, string name) + : base(name, SchemaCommandType.DropForeignKey) { + SrcTable = srcTable; } } } diff --git a/src/Orchard/DataMigration/Schema/DropTableCommand.cs b/src/Orchard/DataMigration/Schema/DropTableCommand.cs index 38d21ecbd..4a8a67222 100644 --- a/src/Orchard/DataMigration/Schema/DropTableCommand.cs +++ b/src/Orchard/DataMigration/Schema/DropTableCommand.cs @@ -1,7 +1,7 @@ namespace Orchard.DataMigration.Schema { public class DropTableCommand : SchemaCommand { public DropTableCommand(string name) - : base(name) { + : base(name, SchemaCommandType.DropTable) { } } } diff --git a/src/Orchard/DataMigration/Schema/ISchemaBuilderCommand.cs b/src/Orchard/DataMigration/Schema/ISchemaBuilderCommand.cs new file mode 100644 index 000000000..45e974ab0 --- /dev/null +++ b/src/Orchard/DataMigration/Schema/ISchemaBuilderCommand.cs @@ -0,0 +1,9 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Orchard.DataMigration.Schema { + public interface ISchemaBuilderCommand { + } +} diff --git a/src/Orchard/DataMigration/Schema/IShellSettings.cs b/src/Orchard/DataMigration/Schema/IShellSettings.cs new file mode 100644 index 000000000..5f282702b --- /dev/null +++ b/src/Orchard/DataMigration/Schema/IShellSettings.cs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Orchard/DataMigration/Schema/SchemaBuilder.cs b/src/Orchard/DataMigration/Schema/SchemaBuilder.cs index 2cd33ba34..c133e59d1 100644 --- a/src/Orchard/DataMigration/Schema/SchemaBuilder.cs +++ b/src/Orchard/DataMigration/Schema/SchemaBuilder.cs @@ -1,44 +1,55 @@ using System; -using System.Collections.Generic; +using Orchard.DataMigration.Interpreters; namespace Orchard.DataMigration.Schema { public class SchemaBuilder { - - private readonly List _schemaCommands; - - public SchemaBuilder() { - _schemaCommands = new List(); + private readonly IDataMigrationInterpreter _interpreter; + public SchemaBuilder(IDataMigrationInterpreter interpreter) { + _interpreter = interpreter; } - public SchemaBuilder(string tablePrefix) : this() { - TablePrefix = tablePrefix; - } - - public string TablePrefix { get; private set; } - public SchemaBuilder CreateTable(string name, Action table) { var createTable = new CreateTableCommand(name); table(createTable); - _schemaCommands.Add(createTable); + Run(createTable); return this; } public SchemaBuilder AlterTable(string name, Action table) { var alterTable = new AlterTableCommand(name); table(alterTable); - _schemaCommands.Add(alterTable); + Run(alterTable); return this; } public SchemaBuilder DropTable(string name) { var deleteTable = new DropTableCommand(name); - _schemaCommands.Add(deleteTable); + Run(deleteTable); return this; } - public SchemaBuilder ExecuteSql(string sql, Action statement) { + public SchemaBuilder ExecuteSql(string sql, Action statement = null) { var sqlStatmentCommand = new SqlStatementCommand(sql); - statement(sqlStatmentCommand); + if ( statement != null ) { + statement(sqlStatmentCommand); + } + Run(sqlStatmentCommand); + return this; + } + + private void Run(SchemaCommand command) { + _interpreter.Visit(command); + } + + public SchemaBuilder CreateForeignKey(string name, string srcTable, string[] srcColumns, string destTable, string[] destColumns) { + var command = new CreateForeignKeyCommand(name, srcTable, srcColumns, destTable, destColumns); + Run(command); + return this; + } + + public SchemaBuilder DropForeignKey(string srcTable, string name) { + var command = new DropForeignKeyCommand(srcTable, name); + Run(command); return this; } diff --git a/src/Orchard/DataMigration/Schema/SchemaCommand.cs b/src/Orchard/DataMigration/Schema/SchemaCommand.cs index 3f4392ad0..64176e935 100644 --- a/src/Orchard/DataMigration/Schema/SchemaCommand.cs +++ b/src/Orchard/DataMigration/Schema/SchemaCommand.cs @@ -1,19 +1,31 @@ using System.Collections.Generic; +using JetBrains.Annotations; namespace Orchard.DataMigration.Schema { - public class SchemaCommand { - protected readonly List _tableCommands; - - public SchemaCommand(string tableName) { - _tableCommands = new List(); - Name(tableName); + public abstract class SchemaCommand : ISchemaBuilderCommand { + protected SchemaCommand(string name, SchemaCommandType type ) { + TableCommands = new List(); + Type = type; + WithName(name); } - public string TableName { get; private set; } + public string Name { get; private set; } + public List TableCommands { get; private set; } - public SchemaCommand Name(string name) { - TableName = name; + public SchemaCommandType Type { get; [UsedImplicitly]private set; } + + public SchemaCommand WithName(string name) { + Name = name; return this; } } + + public enum SchemaCommandType { + CreateTable, + DropTable, + AlterTable, + SqlStatement, + CreateForeignKey, + DropForeignKey + } } diff --git a/src/Orchard/DataMigration/Schema/SqlStatementCommand.cs b/src/Orchard/DataMigration/Schema/SqlStatementCommand.cs index 86721f76f..b730b7863 100644 --- a/src/Orchard/DataMigration/Schema/SqlStatementCommand.cs +++ b/src/Orchard/DataMigration/Schema/SqlStatementCommand.cs @@ -2,17 +2,18 @@ namespace Orchard.DataMigration.Schema { public class SqlStatementCommand : SchemaCommand { - protected readonly List _dialects; + protected readonly List _providers; public SqlStatementCommand(string sql) - : base("") { + : base(string.Empty, SchemaCommandType.SqlStatement) { Sql = sql; - _dialects = new List(); + _providers = new List(); } public string Sql { get; private set; } + public List Providers { get { return _providers; } } - public SqlStatementCommand ForDialect(string dialect) { - _dialects.Add(dialect); + public SqlStatementCommand ForProvider(string dataProvider) { + _providers.Add(dataProvider); return this; } } diff --git a/src/Orchard/DataMigration/Schema/TableCommand.cs b/src/Orchard/DataMigration/Schema/TableCommand.cs index 5e5bb458e..3d4092fec 100644 --- a/src/Orchard/DataMigration/Schema/TableCommand.cs +++ b/src/Orchard/DataMigration/Schema/TableCommand.cs @@ -1,9 +1,9 @@ namespace Orchard.DataMigration.Schema { - public class TableCommand { - private string _name; + public class TableCommand : ISchemaBuilderCommand{ + public string TableName { get; private set; } - public TableCommand(string name) { - _name = name; + public TableCommand(string tableName) { + TableName = tableName; } } diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 01159b3b6..97607c23a 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -349,6 +349,13 @@ Code + + + + + + +