From 98ef1aa3d01f8bc712bfef27930dd839ab743286 Mon Sep 17 00:00:00 2001 From: Andrew Benz Date: Sat, 17 Mar 2012 20:16:23 -0500 Subject: [PATCH] Added the ability to delete multiple columns at once. --- .../Generators/Generic/GenericGenerator.cs | 12 ++- .../Generators/Postgres/PostgresGenerator.cs | 11 +- .../SqlServer/SqlServer2000Generator.cs | 31 ++++-- .../SqlServer/SqlServer2005Generator.cs | 26 ++--- .../SqlServer/SqlServerCeGenerator.cs | 3 +- .../DeleteColumnExpressionBuilderTests.cs | 25 +++++ .../Delete/DeleteExpressionRootTests.cs | 3 +- .../CreateColumnExpressionTests.cs | 3 +- .../DeleteColumnExpressionTests.cs | 16 ++- .../Unit/Generators/GeneratorTestHelper.cs | 7 +- .../Unit/Generators/Jet/JetDropTableTests.cs | 9 ++ .../Generators/MySql/MySqlDropTableTests.cs | 8 ++ .../Generators/Oracle/OracleDropTableTests.cs | 8 ++ .../Postgres/PostgresGeneratorTests.cs | 15 ++- .../Generators/SQLite/SQLiteDropTableTests.cs | 8 ++ .../SqlServer2000DropTableTests.cs | 48 ++++++++- .../SqlServer2005DropTableTests.cs | 102 +++++++++++++++++- .../Column/DeleteColumnExpressionBuilder.cs | 6 ++ .../Column/IDeleteColumnFromTableSyntax.cs | 1 + .../Builders/Delete/DeleteExpressionRoot.cs | 4 +- .../Expressions/CreateColumnExpression.cs | 2 +- .../Expressions/DeleteColumnExpression.cs | 15 ++- src/FluentMigrator/FluentMigrator.csproj | 2 +- .../Infrastructure/ErrorMessages.cs | 1 + 24 files changed, 314 insertions(+), 52 deletions(-) diff --git a/src/FluentMigrator.Runner/Generators/Generic/GenericGenerator.cs b/src/FluentMigrator.Runner/Generators/Generic/GenericGenerator.cs index d10d9de3a..bee871ff2 100644 --- a/src/FluentMigrator.Runner/Generators/Generic/GenericGenerator.cs +++ b/src/FluentMigrator.Runner/Generators/Generic/GenericGenerator.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; +using System.Data; +using System.Linq; using System.Text; using FluentMigrator.Expressions; using FluentMigrator.Model; using FluentMigrator.Runner.Generators.Base; -using System.Linq; -using System.Data; namespace FluentMigrator.Runner.Generators.Generic { @@ -92,7 +92,13 @@ public override string Generate(AlterColumnExpression expression) public override string Generate(DeleteColumnExpression expression) { - return String.Format(DropColumn, Quoter.QuoteTableName(expression.TableName), Quoter.QuoteColumnName(expression.ColumnName)); + StringBuilder builder = new StringBuilder(); + foreach (string columnName in expression.ColumnNames) + { + if (expression.ColumnNames.First() != columnName) builder.AppendLine(";"); + builder.AppendFormat(DropColumn, Quoter.QuoteTableName(expression.TableName), Quoter.QuoteColumnName(columnName)); + } + return builder.ToString(); } public override string Generate(RenameColumnExpression expression) diff --git a/src/FluentMigrator.Runner/Generators/Postgres/PostgresGenerator.cs b/src/FluentMigrator.Runner/Generators/Postgres/PostgresGenerator.cs index 1bfb3a5fe..c7f6f2229 100644 --- a/src/FluentMigrator.Runner/Generators/Postgres/PostgresGenerator.cs +++ b/src/FluentMigrator.Runner/Generators/Postgres/PostgresGenerator.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; using FluentMigrator.Expressions; using FluentMigrator.Model; @@ -44,7 +45,15 @@ public override string Generate(DeleteTableExpression expression) public override string Generate(DeleteColumnExpression expression) { - return string.Format("ALTER TABLE {0}.{1} DROP COLUMN {2}", Quoter.QuoteSchemaName(expression.SchemaName), Quoter.QuoteTableName(expression.TableName), Quoter.QuoteColumnName(expression.ColumnName)); + StringBuilder builder = new StringBuilder(); + foreach (string columnName in expression.ColumnNames) { + if (expression.ColumnNames.First() != columnName) builder.AppendLine(";"); + builder.AppendFormat("ALTER TABLE {0}.{1} DROP COLUMN {2}", + Quoter.QuoteSchemaName(expression.SchemaName), + Quoter.QuoteTableName(expression.TableName), + Quoter.QuoteColumnName(columnName)); + } + return builder.ToString(); } public override string Generate(CreateForeignKeyExpression expression) diff --git a/src/FluentMigrator.Runner/Generators/SqlServer/SqlServer2000Generator.cs b/src/FluentMigrator.Runner/Generators/SqlServer/SqlServer2000Generator.cs index ed1e44227..533a993fe 100644 --- a/src/FluentMigrator.Runner/Generators/SqlServer/SqlServer2000Generator.cs +++ b/src/FluentMigrator.Runner/Generators/SqlServer/SqlServer2000Generator.cs @@ -17,6 +17,7 @@ #endregion using System; +using System.Linq; using System.Text; using FluentMigrator.Expressions; using FluentMigrator.Runner.Extensions; @@ -67,20 +68,28 @@ public override string Generate(DeleteColumnExpression expression) // before we drop a column, we have to drop any default value constraints in SQL Server var builder = new StringBuilder(); - builder.AppendLine(Generate(new DeleteDefaultConstraintExpression - { - ColumnName = expression.ColumnName, - SchemaName = expression.SchemaName, - TableName = expression.TableName - })); + foreach (string column in expression.ColumnNames) + { + if (expression.ColumnNames.First() != column) builder.AppendLine("GO"); + BuildDelete(expression, column, builder); + } + + return builder.ToString(); + } - builder.AppendLine(); + protected virtual void BuildDelete(DeleteColumnExpression expression, string columnName, StringBuilder builder) + { + builder.AppendLine(Generate(new DeleteDefaultConstraintExpression { + ColumnName = columnName, + SchemaName = expression.SchemaName, + TableName = expression.TableName + })); - builder.Append(String.Format("-- now we can finally drop column\r\nALTER TABLE {0} DROP COLUMN {1};", - Quoter.QuoteTableName(expression.TableName), - Quoter.QuoteColumnName(expression.ColumnName))); + builder.AppendLine(); - return builder.ToString(); + builder.AppendLine(String.Format("-- now we can finally drop column\r\nALTER TABLE {0} DROP COLUMN {1};", + Quoter.QuoteTableName(expression.TableName), + Quoter.QuoteColumnName(columnName))); } public override string Generate(AlterDefaultConstraintExpression expression) diff --git a/src/FluentMigrator.Runner/Generators/SqlServer/SqlServer2005Generator.cs b/src/FluentMigrator.Runner/Generators/SqlServer/SqlServer2005Generator.cs index 23a672258..8218c1594 100644 --- a/src/FluentMigrator.Runner/Generators/SqlServer/SqlServer2005Generator.cs +++ b/src/FluentMigrator.Runner/Generators/SqlServer/SqlServer2005Generator.cs @@ -238,26 +238,20 @@ public override string Generate(DeleteIndexExpression expression) return String.Format(DropIndex, Quoter.QuoteIndexName(expression.Index.Name), Quoter.QuoteSchemaName(expression.Index.SchemaName), Quoter.QuoteTableName(expression.Index.TableName)); } - public override string Generate(DeleteColumnExpression expression) + protected override void BuildDelete(DeleteColumnExpression expression, string columnName, StringBuilder builder) { - // before we drop a column, we have to drop any default value constraints in SQL Server - var builder = new StringBuilder(); - - builder.AppendLine(Generate(new DeleteDefaultConstraintExpression - { - ColumnName = expression.ColumnName, - SchemaName = expression.SchemaName, - TableName = expression.TableName - })); + builder.AppendLine(Generate(new DeleteDefaultConstraintExpression { + ColumnName = columnName, + SchemaName = expression.SchemaName, + TableName = expression.TableName + })); builder.AppendLine(); - builder.Append(String.Format("-- now we can finally drop column\r\nALTER TABLE {2}.{0} DROP COLUMN {1};", - Quoter.QuoteTableName(expression.TableName), - Quoter.QuoteColumnName(expression.ColumnName), - Quoter.QuoteSchemaName(expression.SchemaName))); - - return builder.ToString(); + builder.AppendLine(String.Format("-- now we can finally drop column\r\nALTER TABLE {2}.{0} DROP COLUMN {1};", + Quoter.QuoteTableName(expression.TableName), + Quoter.QuoteColumnName(columnName), + Quoter.QuoteSchemaName(expression.SchemaName))); } public override string Generate(AlterDefaultConstraintExpression expression) diff --git a/src/FluentMigrator.Runner/Generators/SqlServer/SqlServerCeGenerator.cs b/src/FluentMigrator.Runner/Generators/SqlServer/SqlServerCeGenerator.cs index 7b0d4bb94..e8b39461b 100644 --- a/src/FluentMigrator.Runner/Generators/SqlServer/SqlServerCeGenerator.cs +++ b/src/FluentMigrator.Runner/Generators/SqlServer/SqlServerCeGenerator.cs @@ -18,6 +18,7 @@ #endregion using System; +using System.Linq; using FluentMigrator.Expressions; namespace FluentMigrator.Runner.Generators.SqlServer @@ -56,7 +57,7 @@ public override string Generate(DeleteColumnExpression expression) { // Limited functionality in CE, for now will just drop the column.. no DECLARE support! const string sql = @"ALTER TABLE {0} DROP COLUMN {1};"; - return String.Format(sql, Quoter.QuoteTableName(expression.TableName), Quoter.QuoteColumnName(expression.ColumnName)); + return String.Format(sql, Quoter.QuoteTableName(expression.TableName), Quoter.QuoteColumnName(expression.ColumnNames.ElementAt(0))); } public override string Generate(DeleteIndexExpression expression) diff --git a/src/FluentMigrator.Tests/Unit/Builders/Delete/DeleteColumnExpressionBuilderTests.cs b/src/FluentMigrator.Tests/Unit/Builders/Delete/DeleteColumnExpressionBuilderTests.cs index 174cf9648..b1f1ff898 100644 --- a/src/FluentMigrator.Tests/Unit/Builders/Delete/DeleteColumnExpressionBuilderTests.cs +++ b/src/FluentMigrator.Tests/Unit/Builders/Delete/DeleteColumnExpressionBuilderTests.cs @@ -16,6 +16,8 @@ // #endregion +using System.Collections.Generic; +using System.Linq; using FluentMigrator.Builders.Delete.Column; using FluentMigrator.Expressions; using Moq; @@ -36,5 +38,28 @@ public void CallingFromTableSetsTableName() expressionMock.VerifySet(x => x.TableName = "Bacon"); } + + [Test] + public void CallingColumnAddsColumnNameToList() + { + var expressionMock = new Mock(); + expressionMock.Object.ColumnNames = new List {"Cheese"}; + + var builder = new DeleteColumnExpressionBuilder(expressionMock.Object); + builder.Column("Bacon"); + + Assert.That(expressionMock.Object.ColumnNames.ElementAt(1), Is.EqualTo("Bacon")); + } + + [Test] + public void CallingInSchemaSetsSchemaOnExpression() + { + var expressionMock = new Mock(); + + var builder = new DeleteColumnExpressionBuilder(expressionMock.Object); + builder.InSchema("Bacon"); + + expressionMock.VerifySet(x => x.SchemaName = "Bacon"); + } } } \ No newline at end of file diff --git a/src/FluentMigrator.Tests/Unit/Builders/Delete/DeleteExpressionRootTests.cs b/src/FluentMigrator.Tests/Unit/Builders/Delete/DeleteExpressionRootTests.cs index f174fab47..ab0a9540f 100644 --- a/src/FluentMigrator.Tests/Unit/Builders/Delete/DeleteExpressionRootTests.cs +++ b/src/FluentMigrator.Tests/Unit/Builders/Delete/DeleteExpressionRootTests.cs @@ -17,6 +17,7 @@ #endregion using System.Collections.Generic; +using System.Linq; using FluentMigrator.Builders.Delete; using FluentMigrator.Builders.Delete.Column; using FluentMigrator.Builders.Delete.ForeignKey; @@ -57,7 +58,7 @@ public void CallingColumnAddsDeleteColumnExpressionToContextWithSpecifiedName() var root = new DeleteExpressionRoot(contextMock.Object); root.Column("Bacon"); - collectionMock.Verify(x => x.Add(It.Is(e => e.ColumnName.Equals("Bacon")))); + collectionMock.Verify(x => x.Add(It.Is(e => e.ColumnNames.ElementAt(0).Equals("Bacon")))); contextMock.VerifyGet(x => x.Expressions); } diff --git a/src/FluentMigrator.Tests/Unit/Expressions/CreateColumnExpressionTests.cs b/src/FluentMigrator.Tests/Unit/Expressions/CreateColumnExpressionTests.cs index 303cd8678..812639f55 100644 --- a/src/FluentMigrator.Tests/Unit/Expressions/CreateColumnExpressionTests.cs +++ b/src/FluentMigrator.Tests/Unit/Expressions/CreateColumnExpressionTests.cs @@ -18,6 +18,7 @@ using System; using System.Data; +using System.Linq; using FluentMigrator.Expressions; using FluentMigrator.Infrastructure; using FluentMigrator.Model; @@ -75,7 +76,7 @@ public void ReverseSetsTableNameAndColumnNameOnGeneratedExpression() var expression = new CreateColumnExpression { TableName = "Bacon", Column = { Name = "BaconId" } }; var reverse = expression.Reverse() as DeleteColumnExpression; reverse.TableName.ShouldBe("Bacon"); - reverse.ColumnName.ShouldBe("BaconId"); + reverse.ColumnNames.ElementAt(0).ShouldBe("BaconId"); } [Test] diff --git a/src/FluentMigrator.Tests/Unit/Expressions/DeleteColumnExpressionTests.cs b/src/FluentMigrator.Tests/Unit/Expressions/DeleteColumnExpressionTests.cs index f16d43ae1..47591e49e 100644 --- a/src/FluentMigrator.Tests/Unit/Expressions/DeleteColumnExpressionTests.cs +++ b/src/FluentMigrator.Tests/Unit/Expressions/DeleteColumnExpressionTests.cs @@ -55,7 +55,7 @@ public void ErrorIsNotReturnedWhenTableNameIsNotNullEmptyString() [Test] public void ErrorIsReturnedWhenColumnNameIsNull() { - var expression = new DeleteColumnExpression { ColumnName = null }; + var expression = new DeleteColumnExpression { ColumnNames = {null} }; var errors = ValidationHelper.CollectErrors(expression); errors.ShouldContain(ErrorMessages.ColumnNameCannotBeNullOrEmpty); } @@ -63,15 +63,23 @@ public void ErrorIsReturnedWhenColumnNameIsNull() [Test] public void ErrorIsReturnedWhenColumnNameIsEmptyString() { - var expression = new DeleteColumnExpression { ColumnName = String.Empty }; + var expression = new DeleteColumnExpression { ColumnNames = {String.Empty} }; var errors = ValidationHelper.CollectErrors(expression); errors.ShouldContain(ErrorMessages.ColumnNameCannotBeNullOrEmpty); } + [Test] + public void ErrorIsReturnedWhenColumnIsSpecifiedMultipleTimes() + { + var expression = new DeleteColumnExpression { ColumnNames = { "Bacon", "Bacon" } }; + var errors = ValidationHelper.CollectErrors(expression); + errors.ShouldContain(ErrorMessages.ColumnNamesMustBeUnique); + } + [Test] public void ErrorIsNotReturnedWhenColumnNameIsNotNullEmptyString() { - var expression = new DeleteColumnExpression { ColumnName = "Bacon" }; + var expression = new DeleteColumnExpression { ColumnNames = {"Bacon"} }; var errors = ValidationHelper.CollectErrors(expression); errors.ShouldNotContain(ErrorMessages.ColumnNameCannotBeNullOrEmpty); } @@ -86,7 +94,7 @@ public void ReverseThrowsException() [Test] public void ToStringIsDescriptive() { - var expression = new DeleteColumnExpression { TableName = "Test", ColumnName = "Bacon" }; + var expression = new DeleteColumnExpression { TableName = "Test", ColumnNames = {"Bacon"} }; expression.ToString().ShouldBe("DeleteColumn Test Bacon"); } } diff --git a/src/FluentMigrator.Tests/Unit/Generators/GeneratorTestHelper.cs b/src/FluentMigrator.Tests/Unit/Generators/GeneratorTestHelper.cs index 98a57a797..3b81ad53e 100644 --- a/src/FluentMigrator.Tests/Unit/Generators/GeneratorTestHelper.cs +++ b/src/FluentMigrator.Tests/Unit/Generators/GeneratorTestHelper.cs @@ -292,7 +292,12 @@ public static DeleteTableExpression GetDeleteTableExpression() public static DeleteColumnExpression GetDeleteColumnExpression() { - return new DeleteColumnExpression { TableName = TestTableName1, ColumnName = TestColumnName1 }; + return GetDeleteColumnExpression(new [] {TestColumnName1}); + } + + public static DeleteColumnExpression GetDeleteColumnExpression(string[] columns) + { + return new DeleteColumnExpression { TableName = TestTableName1, ColumnNames = columns }; } public static DeleteIndexExpression GetDeleteIndexExpression() diff --git a/src/FluentMigrator.Tests/Unit/Generators/Jet/JetDropTableTests.cs b/src/FluentMigrator.Tests/Unit/Generators/Jet/JetDropTableTests.cs index f56b029f4..8fd3fa57f 100644 --- a/src/FluentMigrator.Tests/Unit/Generators/Jet/JetDropTableTests.cs +++ b/src/FluentMigrator.Tests/Unit/Generators/Jet/JetDropTableTests.cs @@ -25,6 +25,15 @@ public override void CanDropColumn() sql.ShouldBe("ALTER TABLE [TestTable1] DROP COLUMN [TestColumn1]"); } + [Test] + public void CanDropMultipleColumns() + { + var expression = GeneratorTestHelper.GetDeleteColumnExpression(new string[] { "TestColumn1", "TestColumn2" }); + + string sql = _generator.Generate(expression); + sql.ShouldBe("ALTER TABLE [TestTable1] DROP COLUMN [TestColumn1];\r\nALTER TABLE [TestTable1] DROP COLUMN [TestColumn2]"); + } + [Test] public override void CanDropForeignKey() { diff --git a/src/FluentMigrator.Tests/Unit/Generators/MySql/MySqlDropTableTests.cs b/src/FluentMigrator.Tests/Unit/Generators/MySql/MySqlDropTableTests.cs index 0407eb204..3635b1184 100644 --- a/src/FluentMigrator.Tests/Unit/Generators/MySql/MySqlDropTableTests.cs +++ b/src/FluentMigrator.Tests/Unit/Generators/MySql/MySqlDropTableTests.cs @@ -24,6 +24,14 @@ public override void CanDropColumn() sql.ShouldBe("ALTER TABLE `TestTable1` DROP COLUMN `TestColumn1`"); } + [Test] + public void CanDropMultipleColumns() + { + var expression = GeneratorTestHelper.GetDeleteColumnExpression(new string[] {"TestColumn1", "TestColumn2"}); + var sql = _generator.Generate(expression); + sql.ShouldBe("ALTER TABLE `TestTable1` DROP COLUMN `TestColumn1`;\r\nALTER TABLE `TestTable1` DROP COLUMN `TestColumn2`"); + } + [Test] public override void CanDropForeignKey() { diff --git a/src/FluentMigrator.Tests/Unit/Generators/Oracle/OracleDropTableTests.cs b/src/FluentMigrator.Tests/Unit/Generators/Oracle/OracleDropTableTests.cs index 193d4c79c..8461eb62f 100644 --- a/src/FluentMigrator.Tests/Unit/Generators/Oracle/OracleDropTableTests.cs +++ b/src/FluentMigrator.Tests/Unit/Generators/Oracle/OracleDropTableTests.cs @@ -24,6 +24,14 @@ public override void CanDropColumn() sql.ShouldBe("ALTER TABLE TestTable1 DROP COLUMN TestColumn1"); } + [Test] + public void CanDropMultipleColumns() + { + var expression = GeneratorTestHelper.GetDeleteColumnExpression(new string[] {"TestColumn1", "TestColumn2"}); + string sql = _generator.Generate(expression); + sql.ShouldBe("ALTER TABLE TestTable1 DROP COLUMN TestColumn1;\r\nALTER TABLE TestTable1 DROP COLUMN TestColumn2"); + } + [Test] public override void CanDropForeignKey() { diff --git a/src/FluentMigrator.Tests/Unit/Generators/Postgres/PostgresGeneratorTests.cs b/src/FluentMigrator.Tests/Unit/Generators/Postgres/PostgresGeneratorTests.cs index cdcef58a8..7bc3c6893 100644 --- a/src/FluentMigrator.Tests/Unit/Generators/Postgres/PostgresGeneratorTests.cs +++ b/src/FluentMigrator.Tests/Unit/Generators/Postgres/PostgresGeneratorTests.cs @@ -157,12 +157,25 @@ public void CanDropColumn() var expression = new DeleteColumnExpression(); expression.TableName = tableName; - expression.ColumnName = columnName; + expression.ColumnNames.Add(columnName); string sql = generator.Generate(expression); sql.ShouldBe("ALTER TABLE \"public\".\"NewTable\" DROP COLUMN \"NewColumn\""); } + [Test] + public void CanDropMultipleColumns() + { + var expression = new DeleteColumnExpression(); + expression.TableName = "NewTable"; + expression.ColumnNames.Add("NewColumn"); + expression.ColumnNames.Add("OtherColumn"); + + string sql = generator.Generate(expression); + sql.ShouldBe("ALTER TABLE \"public\".\"NewTable\" DROP COLUMN \"NewColumn\";\r\n" + + "ALTER TABLE \"public\".\"NewTable\" DROP COLUMN \"OtherColumn\""); + } + [Test] public void CanAddColumn() { diff --git a/src/FluentMigrator.Tests/Unit/Generators/SQLite/SQLiteDropTableTests.cs b/src/FluentMigrator.Tests/Unit/Generators/SQLite/SQLiteDropTableTests.cs index 0072eeb53..fa05915b1 100644 --- a/src/FluentMigrator.Tests/Unit/Generators/SQLite/SQLiteDropTableTests.cs +++ b/src/FluentMigrator.Tests/Unit/Generators/SQLite/SQLiteDropTableTests.cs @@ -25,6 +25,14 @@ public override void CanDropColumn() sql.ShouldBe(String.Empty); //because sqlite doesnt support removing columns } + [Test] + public void CanDropMultipleColumns() + { + var expression = GeneratorTestHelper.GetDeleteColumnExpression(new string[] {"TestColumn1", "TestColumn2"}); + string sql = _generator.Generate(expression); + sql.ShouldBe(String.Empty); //because sqlite doesnt support removing columns + } + [Test] public override void CanDropForeignKey() { diff --git a/src/FluentMigrator.Tests/Unit/Generators/SqlServer2000/SqlServer2000DropTableTests.cs b/src/FluentMigrator.Tests/Unit/Generators/SqlServer2000/SqlServer2000DropTableTests.cs index e1c50c717..377a33384 100644 --- a/src/FluentMigrator.Tests/Unit/Generators/SqlServer2000/SqlServer2000DropTableTests.cs +++ b/src/FluentMigrator.Tests/Unit/Generators/SqlServer2000/SqlServer2000DropTableTests.cs @@ -39,7 +39,53 @@ public override void CanDropColumn() "SET @sql = N'ALTER TABLE [TestTable1] DROP CONSTRAINT ' + @default;\r\n" + "EXEC sp_executesql @sql;\r\n\r\n" + "-- now we can finally drop column\r\n" + - "ALTER TABLE [TestTable1] DROP COLUMN [TestColumn1];"; + "ALTER TABLE [TestTable1] DROP COLUMN [TestColumn1];\r\n"; + + sql.ShouldBe(expected); + } + + [Test] + public void CanDropMultipleColumns() + { + //This does not work if it is a primary key + var expression = GeneratorTestHelper.GetDeleteColumnExpression(new [] {"TestColumn1", "TestColumn2"}); + var sql = _generator.Generate(expression); + + const string expected = "DECLARE @default sysname, @sql nvarchar(4000);\r\n\r\n" + + "-- get name of default constraint\r\n" + + "SELECT @default = name\r\n" + + "FROM sys.default_constraints\r\n" + + "WHERE parent_object_id = object_id('[TestTable1]')\r\n" + "" + + "AND type = 'D'\r\n" + "" + + "AND parent_column_id = (\r\n" + "" + + "SELECT column_id\r\n" + + "FROM sys.columns\r\n" + + "WHERE object_id = object_id('[TestTable1]')\r\n" + + "AND name = 'TestColumn1'\r\n" + + ");\r\n\r\n" + + "-- create alter table command to drop contraint as string and run it\r\n" + + "SET @sql = N'ALTER TABLE [TestTable1] DROP CONSTRAINT ' + @default;\r\n" + + "EXEC sp_executesql @sql;\r\n\r\n" + + "-- now we can finally drop column\r\n" + + "ALTER TABLE [TestTable1] DROP COLUMN [TestColumn1];\r\n" + + "GO\r\n" + + "DECLARE @default sysname, @sql nvarchar(4000);\r\n\r\n" + + "-- get name of default constraint\r\n" + + "SELECT @default = name\r\n" + + "FROM sys.default_constraints\r\n" + + "WHERE parent_object_id = object_id('[TestTable1]')\r\n" + "" + + "AND type = 'D'\r\n" + "" + + "AND parent_column_id = (\r\n" + "" + + "SELECT column_id\r\n" + + "FROM sys.columns\r\n" + + "WHERE object_id = object_id('[TestTable1]')\r\n" + + "AND name = 'TestColumn2'\r\n" + + ");\r\n\r\n" + + "-- create alter table command to drop contraint as string and run it\r\n" + + "SET @sql = N'ALTER TABLE [TestTable1] DROP CONSTRAINT ' + @default;\r\n" + + "EXEC sp_executesql @sql;\r\n\r\n" + + "-- now we can finally drop column\r\n" + + "ALTER TABLE [TestTable1] DROP COLUMN [TestColumn2];\r\n"; sql.ShouldBe(expected); } diff --git a/src/FluentMigrator.Tests/Unit/Generators/SqlServer2005/SqlServer2005DropTableTests.cs b/src/FluentMigrator.Tests/Unit/Generators/SqlServer2005/SqlServer2005DropTableTests.cs index ef3f6a26b..1d589a4b0 100644 --- a/src/FluentMigrator.Tests/Unit/Generators/SqlServer2005/SqlServer2005DropTableTests.cs +++ b/src/FluentMigrator.Tests/Unit/Generators/SqlServer2005/SqlServer2005DropTableTests.cs @@ -1,7 +1,7 @@ -using NUnit.Framework; +using FluentMigrator.Expressions; using FluentMigrator.Runner.Generators.SqlServer; +using NUnit.Framework; using NUnit.Should; -using FluentMigrator.Expressions; namespace FluentMigrator.Tests.Unit.Generators.SqlServer { @@ -40,7 +40,54 @@ public void CanDropColumnWithDefaultSchema() "SET @sql = N'ALTER TABLE [dbo].[TestTable1] DROP CONSTRAINT ' + @default;\r\n" + "EXEC sp_executesql @sql;\r\n\r\n" + "-- now we can finally drop column\r\n" + - "ALTER TABLE [dbo].[TestTable1] DROP COLUMN [TestColumn1];"; + "ALTER TABLE [dbo].[TestTable1] DROP COLUMN [TestColumn1];\r\n"; + + sql.ShouldBe(expected); + } + + [Test] + public void CanDropMultipleColumnsWithDefaultSchema() + { + //This does not work if it is a primary key + var expression = GeneratorTestHelper.GetDeleteColumnExpression(new string[] {"TestColumn1", "TestColumn2"}); + + var sql = generator.Generate(expression); + + const string expected = "DECLARE @default sysname, @sql nvarchar(max);\r\n\r\n" + + "-- get name of default constraint\r\n" + + "SELECT @default = name\r\n" + + "FROM sys.default_constraints\r\n" + + "WHERE parent_object_id = object_id('[dbo].[TestTable1]')\r\n" + "" + + "AND type = 'D'\r\n" + "" + + "AND parent_column_id = (\r\n" + "" + + "SELECT column_id\r\n" + + "FROM sys.columns\r\n" + + "WHERE object_id = object_id('[dbo].[TestTable1]')\r\n" + + "AND name = 'TestColumn1'\r\n" + + ");\r\n\r\n" + + "-- create alter table command to drop contraint as string and run it\r\n" + + "SET @sql = N'ALTER TABLE [dbo].[TestTable1] DROP CONSTRAINT ' + @default;\r\n" + + "EXEC sp_executesql @sql;\r\n\r\n" + + "-- now we can finally drop column\r\n" + + "ALTER TABLE [dbo].[TestTable1] DROP COLUMN [TestColumn1];\r\n" + + "GO\r\n" + + "DECLARE @default sysname, @sql nvarchar(max);\r\n\r\n" + + "-- get name of default constraint\r\n" + + "SELECT @default = name\r\n" + + "FROM sys.default_constraints\r\n" + + "WHERE parent_object_id = object_id('[dbo].[TestTable1]')\r\n" + "" + + "AND type = 'D'\r\n" + "" + + "AND parent_column_id = (\r\n" + "" + + "SELECT column_id\r\n" + + "FROM sys.columns\r\n" + + "WHERE object_id = object_id('[dbo].[TestTable1]')\r\n" + + "AND name = 'TestColumn2'\r\n" + + ");\r\n\r\n" + + "-- create alter table command to drop contraint as string and run it\r\n" + + "SET @sql = N'ALTER TABLE [dbo].[TestTable1] DROP CONSTRAINT ' + @default;\r\n" + + "EXEC sp_executesql @sql;\r\n\r\n" + + "-- now we can finally drop column\r\n" + + "ALTER TABLE [dbo].[TestTable1] DROP COLUMN [TestColumn2];\r\n"; sql.ShouldBe(expected); } @@ -95,7 +142,54 @@ public void CanDropColumnWithCustomSchema() "SET @sql = N'ALTER TABLE [TestSchema].[TestTable1] DROP CONSTRAINT ' + @default;\r\n" + "EXEC sp_executesql @sql;\r\n\r\n" + "-- now we can finally drop column\r\n" + - "ALTER TABLE [TestSchema].[TestTable1] DROP COLUMN [TestColumn1];"; + "ALTER TABLE [TestSchema].[TestTable1] DROP COLUMN [TestColumn1];\r\n"; + + sql.ShouldBe(expected); + } + + [Test] + public void CanDropMultipleColumnsWithCustomSchema() + { + //This does not work if it is a primary key + var expression = GeneratorTestHelper.GetDeleteColumnExpression(new string[] {"TestColumn1", "TestColumn2"}); + expression.SchemaName = "TestSchema"; + var sql = generator.Generate(expression); + + const string expected = "DECLARE @default sysname, @sql nvarchar(max);\r\n\r\n" + + "-- get name of default constraint\r\n" + + "SELECT @default = name\r\n" + + "FROM sys.default_constraints\r\n" + + "WHERE parent_object_id = object_id('[TestSchema].[TestTable1]')\r\n" + "" + + "AND type = 'D'\r\n" + "" + + "AND parent_column_id = (\r\n" + "" + + "SELECT column_id\r\n" + + "FROM sys.columns\r\n" + + "WHERE object_id = object_id('[TestSchema].[TestTable1]')\r\n" + + "AND name = 'TestColumn1'\r\n" + + ");\r\n\r\n" + + "-- create alter table command to drop contraint as string and run it\r\n" + + "SET @sql = N'ALTER TABLE [TestSchema].[TestTable1] DROP CONSTRAINT ' + @default;\r\n" + + "EXEC sp_executesql @sql;\r\n\r\n" + + "-- now we can finally drop column\r\n" + + "ALTER TABLE [TestSchema].[TestTable1] DROP COLUMN [TestColumn1];\r\n" + + "GO\r\n" + + "DECLARE @default sysname, @sql nvarchar(max);\r\n\r\n" + + "-- get name of default constraint\r\n" + + "SELECT @default = name\r\n" + + "FROM sys.default_constraints\r\n" + + "WHERE parent_object_id = object_id('[TestSchema].[TestTable1]')\r\n" + "" + + "AND type = 'D'\r\n" + "" + + "AND parent_column_id = (\r\n" + "" + + "SELECT column_id\r\n" + + "FROM sys.columns\r\n" + + "WHERE object_id = object_id('[TestSchema].[TestTable1]')\r\n" + + "AND name = 'TestColumn2'\r\n" + + ");\r\n\r\n" + + "-- create alter table command to drop contraint as string and run it\r\n" + + "SET @sql = N'ALTER TABLE [TestSchema].[TestTable1] DROP CONSTRAINT ' + @default;\r\n" + + "EXEC sp_executesql @sql;\r\n\r\n" + + "-- now we can finally drop column\r\n" + + "ALTER TABLE [TestSchema].[TestTable1] DROP COLUMN [TestColumn2];\r\n"; sql.ShouldBe(expected); } diff --git a/src/FluentMigrator/Builders/Delete/Column/DeleteColumnExpressionBuilder.cs b/src/FluentMigrator/Builders/Delete/Column/DeleteColumnExpressionBuilder.cs index 66107b741..4aff494d9 100644 --- a/src/FluentMigrator/Builders/Delete/Column/DeleteColumnExpressionBuilder.cs +++ b/src/FluentMigrator/Builders/Delete/Column/DeleteColumnExpressionBuilder.cs @@ -34,6 +34,12 @@ public IInSchemaSyntax FromTable(string tableName) return this; } + public IDeleteColumnFromTableSyntax Column(string columnName) + { + Expression.ColumnNames.Add(columnName); + return this; + } + public void InSchema(string schemaName) { Expression.SchemaName = schemaName; diff --git a/src/FluentMigrator/Builders/Delete/Column/IDeleteColumnFromTableSyntax.cs b/src/FluentMigrator/Builders/Delete/Column/IDeleteColumnFromTableSyntax.cs index d88a24ad5..a9b2a9432 100644 --- a/src/FluentMigrator/Builders/Delete/Column/IDeleteColumnFromTableSyntax.cs +++ b/src/FluentMigrator/Builders/Delete/Column/IDeleteColumnFromTableSyntax.cs @@ -23,5 +23,6 @@ namespace FluentMigrator.Builders.Delete.Column public interface IDeleteColumnFromTableSyntax : IFluentSyntax { IInSchemaSyntax FromTable(string tableName); + IDeleteColumnFromTableSyntax Column(string columnName); } } \ No newline at end of file diff --git a/src/FluentMigrator/Builders/Delete/DeleteExpressionRoot.cs b/src/FluentMigrator/Builders/Delete/DeleteExpressionRoot.cs index efdd194a2..600aca097 100644 --- a/src/FluentMigrator/Builders/Delete/DeleteExpressionRoot.cs +++ b/src/FluentMigrator/Builders/Delete/DeleteExpressionRoot.cs @@ -17,6 +17,7 @@ #endregion using FluentMigrator.Builders.Delete.Column; +using FluentMigrator.Builders.Delete.Constraint; using FluentMigrator.Builders.Delete.DefaultConstraint; using FluentMigrator.Builders.Delete.ForeignKey; using FluentMigrator.Builders.Delete.Index; @@ -24,7 +25,6 @@ using FluentMigrator.Builders.Delete.Table; using FluentMigrator.Expressions; using FluentMigrator.Infrastructure; -using FluentMigrator.Builders.Delete.Constraint; using FluentMigrator.Model; namespace FluentMigrator.Builders.Delete @@ -53,7 +53,7 @@ public IInSchemaSyntax Table(string tableName) public IDeleteColumnFromTableSyntax Column(string columnName) { - var expression = new DeleteColumnExpression {ColumnName = columnName}; + var expression = new DeleteColumnExpression {ColumnNames = {columnName}}; _context.Expressions.Add(expression); return new DeleteColumnExpressionBuilder(expression); } diff --git a/src/FluentMigrator/Expressions/CreateColumnExpression.cs b/src/FluentMigrator/Expressions/CreateColumnExpression.cs index f96fafc5f..c87b3e28a 100644 --- a/src/FluentMigrator/Expressions/CreateColumnExpression.cs +++ b/src/FluentMigrator/Expressions/CreateColumnExpression.cs @@ -59,7 +59,7 @@ public override IMigrationExpression Reverse() { SchemaName = SchemaName, TableName = TableName, - ColumnName = Column.Name + ColumnNames = {Column.Name} }; } diff --git a/src/FluentMigrator/Expressions/DeleteColumnExpression.cs b/src/FluentMigrator/Expressions/DeleteColumnExpression.cs index 09881bea9..ae736a2f6 100644 --- a/src/FluentMigrator/Expressions/DeleteColumnExpression.cs +++ b/src/FluentMigrator/Expressions/DeleteColumnExpression.cs @@ -18,23 +18,32 @@ using System; using System.Collections.Generic; +using System.Linq; using FluentMigrator.Infrastructure; namespace FluentMigrator.Expressions { public class DeleteColumnExpression : MigrationExpressionBase { + public DeleteColumnExpression() + { + ColumnNames = new List(); + } + public virtual string SchemaName { get; set; } public virtual string TableName { get; set; } - public virtual string ColumnName { get; set; } + public ICollection ColumnNames { get; set; } public override void CollectValidationErrors(ICollection errors) { if (String.IsNullOrEmpty(TableName)) errors.Add(ErrorMessages.TableNameCannotBeNullOrEmpty); - if (String.IsNullOrEmpty(ColumnName)) + if (ColumnNames == null || !ColumnNames.Any() || ColumnNames.Any(string.IsNullOrEmpty)) errors.Add(ErrorMessages.ColumnNameCannotBeNullOrEmpty); + + if (ColumnNames != null && ColumnNames.GroupBy(x => x).Any(x => x.Count() > 1)) + errors.Add(ErrorMessages.ColumnNamesMustBeUnique); } public override void ExecuteWith(IMigrationProcessor processor) @@ -44,7 +53,7 @@ public override void ExecuteWith(IMigrationProcessor processor) public override string ToString() { - return base.ToString() + TableName + " " + ColumnName; + return base.ToString() + TableName + " " + ColumnNames.Aggregate((a, b) => a + ", " + b); } } } \ No newline at end of file diff --git a/src/FluentMigrator/FluentMigrator.csproj b/src/FluentMigrator/FluentMigrator.csproj index a9da02f45..173ac7a28 100644 --- a/src/FluentMigrator/FluentMigrator.csproj +++ b/src/FluentMigrator/FluentMigrator.csproj @@ -341,4 +341,4 @@ --> - + \ No newline at end of file diff --git a/src/FluentMigrator/Infrastructure/ErrorMessages.cs b/src/FluentMigrator/Infrastructure/ErrorMessages.cs index a967cd7c0..87ee25386 100644 --- a/src/FluentMigrator/Infrastructure/ErrorMessages.cs +++ b/src/FluentMigrator/Infrastructure/ErrorMessages.cs @@ -23,6 +23,7 @@ public static class ErrorMessages { public const string ColumnNameCannotBeNullOrEmpty = "The column's name cannot be null or an empty string"; public const string ColumnTypeMustBeDefined = "The column does not have a type defined"; + public const string ColumnNamesMustBeUnique = "Column names must be unique"; public const string SchemaNameCannotBeNullOrEmpty = "The schema's name cannot be null or an empty string"; public const string TableNameCannotBeNullOrEmpty = "The table's name cannot be null or an empty string"; public const string OldColumnNameCannotBeNullOrEmpty = "The old column name cannot be null or empty";