From 362c280819cadf79e5482de17cb1428ea2ce65cd Mon Sep 17 00:00:00 2001 From: Alessandro Magistroni <65421435+AMagistroni@users.noreply.github.com> Date: Wed, 6 Oct 2021 00:02:41 +0200 Subject: [PATCH] sp_rename columns --- .../Common/TSqlResultProcessDbObject.cs | 11 +++--- .../DbStructures/DbObject.cs | 3 ++ .../DbStructures/Operation.cs | 3 +- .../TSql/CaseInsensitiveComparer.cs | 20 ++++++++++ .../TSql/TSqlSchemaBuilder.cs | 14 ++++++- SqlSchemaCompare.Core/UpdateSchemaManager.cs | 39 ++++++++++--------- SqlSchemaCompare.Test/TSql/TSqlTableTest.cs | 21 ++++++++++ 7 files changed, 85 insertions(+), 26 deletions(-) create mode 100644 SqlSchemaCompare.Core/TSql/CaseInsensitiveComparer.cs diff --git a/SqlSchemaCompare.Core/Common/TSqlResultProcessDbObject.cs b/SqlSchemaCompare.Core/Common/TSqlResultProcessDbObject.cs index f7b8b1c..7b9be8e 100644 --- a/SqlSchemaCompare.Core/Common/TSqlResultProcessDbObject.cs +++ b/SqlSchemaCompare.Core/Common/TSqlResultProcessDbObject.cs @@ -10,17 +10,18 @@ public class OperationOnDbObject { public DbObject DbObject { get; init; } public Operation Operation { get; init; } + public string Parameter { get; init; } } - public List OperationsOnDbObject { get; private set; } = new(); - public void AddOperation(DbObject dbObjects, Operation operation) where T : DbObject + public List OperationsOnDbObject { get; } = new(); + public void AddOperation(DbObject dbObjects, Operation operation, string parameter = null) where T : DbObject { - OperationsOnDbObject.Add(new OperationOnDbObject { DbObject = dbObjects, Operation = operation } ); + OperationsOnDbObject.Add(new OperationOnDbObject { DbObject = dbObjects, Operation = operation, Parameter = parameter } ); } public void AddOperation(IList dbObjects, Operation operation) where T : DbObject { dbObjects.ToList().ForEach(x => AddOperation(x, operation)); - } - + } + public IEnumerable GetDbObject(DbObjectType dbObjectType, Operation operation) { return OperationsOnDbObject.Where(x => x.DbObject.DbObjectType == dbObjectType && x.Operation == operation).Select(x => x.DbObject); diff --git a/SqlSchemaCompare.Core/DbStructures/DbObject.cs b/SqlSchemaCompare.Core/DbStructures/DbObject.cs index 73414bd..6e95505 100644 --- a/SqlSchemaCompare.Core/DbStructures/DbObject.cs +++ b/SqlSchemaCompare.Core/DbStructures/DbObject.cs @@ -6,6 +6,7 @@ public abstract class DbObject public abstract DbObjectType DbObjectType { get; } public string Schema { get; init; } public string Name { get; init; } + public string NameCaseInsensitive => Name.ToLower(); public string Sql { get; set; } public string ParentName { get; init; } @@ -47,5 +48,7 @@ public string Identifier return string.IsNullOrEmpty(Schema) ? Name : $"{Schema}.{Name}"; } } + + public string IdentifierCaseInsensitive => Identifier.ToLower(); } } diff --git a/SqlSchemaCompare.Core/DbStructures/Operation.cs b/SqlSchemaCompare.Core/DbStructures/Operation.cs index c71c50c..b75d7ad 100644 --- a/SqlSchemaCompare.Core/DbStructures/Operation.cs +++ b/SqlSchemaCompare.Core/DbStructures/Operation.cs @@ -6,6 +6,7 @@ public enum Operation Alter, Drop, Enabled, - Disabled + Disabled, + Rename } } diff --git a/SqlSchemaCompare.Core/TSql/CaseInsensitiveComparer.cs b/SqlSchemaCompare.Core/TSql/CaseInsensitiveComparer.cs new file mode 100644 index 0000000..bc25616 --- /dev/null +++ b/SqlSchemaCompare.Core/TSql/CaseInsensitiveComparer.cs @@ -0,0 +1,20 @@ +using SqlSchemaCompare.Core.DbStructures; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace SqlSchemaCompare.Core.TSql +{ + public class CaseInsensitiveComparer : IEqualityComparer + { + public bool Equals(DbObject x, DbObject y) + { + return string.Equals(x.Sql, y.Sql, StringComparison.OrdinalIgnoreCase); + } + + public int GetHashCode([DisallowNull] DbObject obj) + { + return obj.GetHashCode(); + } + } +} diff --git a/SqlSchemaCompare.Core/TSql/TSqlSchemaBuilder.cs b/SqlSchemaCompare.Core/TSql/TSqlSchemaBuilder.cs index 0f42f25..20b4fd7 100644 --- a/SqlSchemaCompare.Core/TSql/TSqlSchemaBuilder.cs +++ b/SqlSchemaCompare.Core/TSql/TSqlSchemaBuilder.cs @@ -17,7 +17,7 @@ public string Build(DbObject dbObject, Operation operation, ResultProcessDbObjec DbObjectType.TableDefaultContraint => BuildTableConstraint(dbObject as TableConstraint, operation, resultProcessDbObject), DbObjectType.TablePrimaryKeyContraint => BuildTableConstraint(dbObject as TableConstraint, operation, resultProcessDbObject), DbObjectType.TableForeignKeyContraint => BuildTableConstraint(dbObject as TableConstraint, operation, resultProcessDbObject), - DbObjectType.Column => BuildColumn(dbObject as Table.Column, operation), + DbObjectType.Column => BuildColumn(dbObject as Table.Column, operation, resultProcessDbObject), DbObjectType.View => BuildView(dbObject as View, operation), DbObjectType.StoreProcedure => BuildGenericDbObjects("PROCEDURE", dbObject as StoreProcedure, operation), DbObjectType.Function => BuildGenericDbObjects("FUNCTION", dbObject as Function, operation), @@ -97,7 +97,7 @@ private string BuildMember(Member member, Operation operation) }; } - private string BuildColumn(Table.Column column, Operation operation) + private string BuildColumn(Table.Column column, Operation operation, ResultProcessDbObject resultProcessDbObject) { switch (operation) { @@ -113,11 +113,21 @@ private string BuildColumn(Table.Column column, Operation operation) return $"ALTER TABLE {column.ParentName} ALTER COLUMN {column.Sql}"; case Operation.Drop: return $"ALTER TABLE {column.ParentName} DROP COLUMN {column.Name}"; + case Operation.Rename: + var dbObject = resultProcessDbObject.OperationsOnDbObject.Single(x => x.Operation == Operation.Rename && x.DbObject.ParentName == column.ParentName); + return $"sp_rename '{column.ParentName}.{dbObject.Parameter}', '{GetStringWithoutBracket(column.Name)}', 'COLUMN'"; default: throw new NotSupportedException("Alter not supported on schema"); } } + private string GetStringWithoutBracket(string value) + { + value = value.StartsWith('[') ? value[1..] : value; + value = value.EndsWith(']') ? value[0..^1] : value; + return value; + } + private string BuildUser(User user, Operation operation) { switch (operation) diff --git a/SqlSchemaCompare.Core/UpdateSchemaManager.cs b/SqlSchemaCompare.Core/UpdateSchemaManager.cs index ce99b27..f3b0986 100644 --- a/SqlSchemaCompare.Core/UpdateSchemaManager.cs +++ b/SqlSchemaCompare.Core/UpdateSchemaManager.cs @@ -1,5 +1,6 @@ using SqlSchemaCompare.Core.Common; using SqlSchemaCompare.Core.DbStructures; +using SqlSchemaCompare.Core.TSql; using System.Collections.Generic; using System.Linq; using System.Text; @@ -34,6 +35,7 @@ public UpdateSchemaManager(ISchemaBuilder schemaBuilder) (DbObjectType.TableForeignKeyContraint, Operation.Drop), (DbObjectType.TablePrimaryKeyContraint, Operation.Drop), (DbObjectType.Index, Operation.Drop), + (DbObjectType.Column, Operation.Rename), (DbObjectType.Column, Operation.Create), (DbObjectType.Column, Operation.Drop), (DbObjectType.Column, Operation.Alter), @@ -98,15 +100,7 @@ public string UpdateSchema(IEnumerable sourceObjects, IEnumerable resultProcessDbObject.GetDbObject(objectToWrite.DbObjectType, Operation.Alter), - Operation.Create => resultProcessDbObject.GetDbObject(objectToWrite.DbObjectType, Operation.Create), - Operation.Disabled => resultProcessDbObject.GetDbObject(objectToWrite.DbObjectType, Operation.Disabled), - Operation.Enabled => resultProcessDbObject.GetDbObject(objectToWrite.DbObjectType, Operation.Enabled), - Operation.Drop => resultProcessDbObject.GetDbObject(objectToWrite.DbObjectType, Operation.Drop), - _ => throw new System.NotSupportedException(), - }; + var dbObjects = resultProcessDbObject.GetDbObject(objectToWrite.DbObjectType, objectToWrite.Operation); foreach (var dbObject in dbObjects.ToList()) { @@ -235,7 +229,7 @@ private void ProcessTable(IEnumerable sourceObjects, IEnumerable(tableOrigin, Operation.Create); - resultProcessDbObject.AddOperation(tableOrigin.TableSetList, Operation.Create); + resultProcessDbObject.AddOperation(tableOrigin.TableSetList, Operation.Create); } else { @@ -272,7 +266,7 @@ private void ProcessTable(IEnumerable sourceObjects, IEnumerable(tableOrigin.TableSetList.Except(destinationTable.TableSetList).ToList(), Operation.Create); + resultProcessDbObject.AddOperation(tableOrigin.TableSetList.Except(destinationTable.TableSetList).ToList(), Operation.Create); } } DropDbObject(originDb, destinationDb, resultProcessDbObject); @@ -282,24 +276,33 @@ private void ProcessTableColumn(Table table, Table destinationTable, ResultProce IList columnsToAdd; IList columnsToDrop; IList columnsToAlter; + IList columnsToRename; + + var columnsEqualsCaseInsensitive = table.Columns.Select(x => x.NameCaseInsensitive).Intersect(destinationTable.Columns.Select(x => x.NameCaseInsensitive)); + columnsToRename = table.Columns.Where(x => columnsEqualsCaseInsensitive.Contains(x.NameCaseInsensitive) && !destinationTable.Columns.Select(x => x.Name).Contains(x.Name)).ToList(); + foreach (var column in columnsToRename) + { + resultProcessDbObject.AddOperation(column, Operation.Rename, + destinationTable.Columns.Single(x => x.IdentifierCaseInsensitive == column.IdentifierCaseInsensitive).Name); + } columnsToAdd = table.Columns .Where(x => table.Columns - .Select(x => x.Name) - .Except(destinationTable.Columns.Select(x => x.Name)) - .Contains(x.Name)).ToList(); + .Select(x => x.NameCaseInsensitive) + .Except(destinationTable.Columns.Select(x => x.NameCaseInsensitive)) + .Contains(x.NameCaseInsensitive)).ToList(); columnsToDrop = destinationTable.Columns .Where(x => destinationTable.Columns - .Select(x => x.Name) - .Except(table.Columns.Select(x => x.Name)) - .Contains(x.Name)).ToList(); + .Select(x => x.NameCaseInsensitive) + .Except(table.Columns.Select(x => x.NameCaseInsensitive)) + .Contains(x.NameCaseInsensitive)).ToList(); columnsToAlter = table.Columns .Except(columnsToAdd) - .Where(x => !destinationTable.Columns.Contains(x)).ToList(); + .Where(x => !destinationTable.Columns.Contains(x, new CaseInsensitiveComparer())).ToList(); resultProcessDbObject.AddOperation(columnsToAdd, Operation.Create); resultProcessDbObject.AddOperation(columnsToAlter, Operation.Alter); diff --git a/SqlSchemaCompare.Test/TSql/TSqlTableTest.cs b/SqlSchemaCompare.Test/TSql/TSqlTableTest.cs index f64ebe6..d58a106 100644 --- a/SqlSchemaCompare.Test/TSql/TSqlTableTest.cs +++ b/SqlSchemaCompare.Test/TSql/TSqlTableTest.cs @@ -705,6 +705,27 @@ ALTER TABLE [schema].[tbl] SET (LOCK_ESCALATION = DISABLE) ALTER TABLE [schema].[tbl] SET (LOCK_ESCALATION = DISABLE) GO +"); + errors.ShouldBeEmpty(); + } + + [Fact] + public void ColumnsDifferentCase() + { + const string origin = +@"CREATE TABLE [schema].[tbl] ([col1] INT not null) +GO"; + + const string destination = +@"CREATE TABLE [schema].[tbl] ([COL1] INT not null) +GO"; + + (string updateSchema, string errors) = UtilityTest.UpdateSchema(origin, destination, SelectedObjects); + + updateSchema.ShouldBe( +@"sp_rename '[schema].[tbl].[COL1]', 'col1', 'COLUMN' +GO + "); errors.ShouldBeEmpty(); }