From e5ace3cfed6d0518897c2523a543a276fb3a75f6 Mon Sep 17 00:00:00 2001 From: Paul Welter Date: Tue, 9 Jul 2024 15:28:35 -0500 Subject: [PATCH] fix merge bug --- .../Merge/DataMergeDefinition.cs | 2 +- .../Merge/DataMergeGenerator.cs | 2 +- test/FluentCommand.Entities/Member.cs | 22 ++++++ .../DataMergeGeneratorTests.cs | 72 ++++++++++++++++--- .../Scripts/Script001.Tracker.Schema.sql | 10 +++ ...s.BuildMergeDataMismatchTests.verified.txt | 40 +++++++++++ ...sts.BuildMergeDataOutputTests.verified.txt | 56 +++++++++++++++ ...atorTests.BuildMergeDataTests.verified.txt | 42 +++++++++++ ...eneratorTests.BuildMergeTests.verified.txt | 44 ++++++++++++ 9 files changed, 278 insertions(+), 12 deletions(-) create mode 100644 test/FluentCommand.Entities/Member.cs create mode 100644 test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeDataMismatchTests.verified.txt create mode 100644 test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeDataOutputTests.verified.txt create mode 100644 test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeDataTests.verified.txt create mode 100644 test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeTests.verified.txt diff --git a/src/FluentCommand.SqlServer/Merge/DataMergeDefinition.cs b/src/FluentCommand.SqlServer/Merge/DataMergeDefinition.cs index 88bcfa61..103ed931 100644 --- a/src/FluentCommand.SqlServer/Merge/DataMergeDefinition.cs +++ b/src/FluentCommand.SqlServer/Merge/DataMergeDefinition.cs @@ -121,7 +121,7 @@ public static void AutoMap(DataMergeDefinition mergeDefinition) foreach (var property in typeAccessor.GetProperties()) { - string sourceColumn = property.Name; + string sourceColumn = property.Column; string targetColumn = property.Column; string nativeType = property.ColumnType ?? SqlTypeMapping.NativeType(property.MemberType); diff --git a/src/FluentCommand.SqlServer/Merge/DataMergeGenerator.cs b/src/FluentCommand.SqlServer/Merge/DataMergeGenerator.cs index 5abef28f..3c6ebad6 100644 --- a/src/FluentCommand.SqlServer/Merge/DataMergeGenerator.cs +++ b/src/FluentCommand.SqlServer/Merge/DataMergeGenerator.cs @@ -178,7 +178,7 @@ private static void AppendUsingData(DataMergeDefinition mergeDefinition, List wroteRow) .Append(' ', TabSize) - .Append("("); + .Append("("); for (int i = 0; i < reader.FieldCount; i++) { diff --git a/test/FluentCommand.Entities/Member.cs b/test/FluentCommand.Entities/Member.cs new file mode 100644 index 00000000..d9e7df32 --- /dev/null +++ b/test/FluentCommand.Entities/Member.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace FluentCommand.Entities; + +[Table("member_user", Schema = "dbo")] +public class Member +{ + [Column("Id")] + public Guid Id { get; set; } + + [Column("email_address")] + public string EmailAddress { get; set; } + + [Column("display_name")] + public string DisplayName { get; set; } + + [Column("first_name")] + public string FirstName { get; set; } + + [Column("last_name")] + public string LastName { get; set; } +} diff --git a/test/FluentCommand.SqlServer.Tests/DataMergeGeneratorTests.cs b/test/FluentCommand.SqlServer.Tests/DataMergeGeneratorTests.cs index 5e663e0b..62e156ba 100644 --- a/test/FluentCommand.SqlServer.Tests/DataMergeGeneratorTests.cs +++ b/test/FluentCommand.SqlServer.Tests/DataMergeGeneratorTests.cs @@ -61,7 +61,7 @@ public void QuoteIdentifier() } [Fact] - public void BuildMergeTests() + public async System.Threading.Tasks.Task BuildMergeTests() { var definition = new DataMergeDefinition(); @@ -79,13 +79,15 @@ public void BuildMergeTests() var sql = DataMergeGenerator.BuildMerge(definition); sql.Should().NotBeNullOrEmpty(); - Output.WriteLine("MergeStatement:"); - Output.WriteLine(sql); + await Verifier + .Verify(sql) + .UseDirectory("Snapshots") + .AddScrubber(scrubber => scrubber.Replace(definition.TemporaryTable, "#MergeTable")); } [Fact] - public void BuildMergeDataTests() + public async System.Threading.Tasks.Task BuildMergeDataTests() { var definition = new DataMergeDefinition(); @@ -123,8 +125,9 @@ public void BuildMergeDataTests() var sql = DataMergeGenerator.BuildMerge(definition, listDataReader); sql.Should().NotBeNullOrEmpty(); - Output.WriteLine("MergeStatement:"); - Output.WriteLine(sql); + await Verifier + .Verify(sql) + .UseDirectory("Snapshots"); } [Fact] @@ -248,7 +251,7 @@ await Verifier } [Fact] - public void BuildMergeDataOutputTests() + public async System.Threading.Tasks.Task BuildMergeDataOutputTests() { var definition = new DataMergeDefinition(); @@ -282,7 +285,7 @@ public void BuildMergeDataOutputTests() }, new UserImport { - EmailAddress = $"random.{DateTime.Now.Ticks}@email.com", + EmailAddress = $"random@email.com", DisplayName = "Random User", FirstName = "Random", LastName = "User" @@ -294,8 +297,57 @@ public void BuildMergeDataOutputTests() var sql = DataMergeGenerator.BuildMerge(definition, dataTable); sql.Should().NotBeNullOrEmpty(); - Output.WriteLine("MergeStatement:"); - Output.WriteLine(sql); + await Verifier + .Verify(sql) + .UseDirectory("Snapshots"); } + [Fact] + public async System.Threading.Tasks.Task BuildMergeDataMismatchTests() + { + var definition = new DataMergeDefinition(); + + DataMergeDefinition.AutoMap(definition); + definition.Columns.Should().NotBeNullOrEmpty(); + + var column = definition.Columns.Find(c => c.SourceColumn == "email_address"); + column.Should().NotBeNull(); + + column.IsKey = true; + column.CanUpdate = false; + + var users = new List + { + new Member + { + EmailAddress = "test@email.com", + DisplayName = "Test User", + FirstName = "Test", + LastName = "User" + }, + new Member + { + EmailAddress = "blah@email.com", + DisplayName = "Blah User", + FirstName = "Blah", + LastName = "User" + }, + new Member + { + EmailAddress = $"random@email.com", + DisplayName = "Random User", + FirstName = "Random", + LastName = "User" + } + }; + + var dataTable = new ListDataReader(users); + + var mergeDataStatement = DataMergeGenerator.BuildMerge(definition, dataTable); + mergeDataStatement.Should().NotBeNullOrEmpty(); + + await Verifier + .Verify(mergeDataStatement) + .UseDirectory("Snapshots"); + } } diff --git a/test/FluentCommand.SqlServer.Tests/Scripts/Script001.Tracker.Schema.sql b/test/FluentCommand.SqlServer.Tests/Scripts/Script001.Tracker.Schema.sql index 273510df..f7ac722c 100644 --- a/test/FluentCommand.SqlServer.Tests/Scripts/Script001.Tracker.Schema.sql +++ b/test/FluentCommand.SqlServer.Tests/Scripts/Script001.Tracker.Schema.sql @@ -115,6 +115,16 @@ CREATE TABLE [dbo].[User] ( CONSTRAINT [PK_User] PRIMARY KEY ([Id]) ); +IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[member_user]') AND type in (N'U')) +CREATE TABLE [dbo].[member_user] ( + [Id] uniqueidentifier NOT NULL DEFAULT (NEWSEQUENTIALID()), + [email_address] nvarchar(256) NOT NULL, + [display_name] nvarchar(256) NOT NULL, + [first_name] nvarchar(256) NULL, + [last_name] nvarchar(256) NULL, + CONSTRAINT [PK_member_user] PRIMARY KEY ([Id]) +); + IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[UserLogin]') AND type in (N'U')) CREATE TABLE [dbo].[UserLogin] ( [Id] uniqueidentifier NOT NULL DEFAULT (NEWSEQUENTIALID()), diff --git a/test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeDataMismatchTests.verified.txt b/test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeDataMismatchTests.verified.txt new file mode 100644 index 00000000..84974120 --- /dev/null +++ b/test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeDataMismatchTests.verified.txt @@ -0,0 +1,40 @@ +MERGE INTO [dbo].[member_user] AS t +USING +( + VALUES + ('00000000-0000-0000-0000-000000000000', 'test@email.com', 'Test User', 'Test', 'User'), + ('00000000-0000-0000-0000-000000000000', 'blah@email.com', 'Blah User', 'Blah', 'User'), + ('00000000-0000-0000-0000-000000000000', 'random@email.com', 'Random User', 'Random', 'User') +) +AS s +( + [Id], [email_address], [display_name], [first_name], [last_name] +) +ON +( + t.[email_address] = s.[email_address] +) +WHEN NOT MATCHED BY TARGET THEN + INSERT + ( + [Id], + [email_address], + [display_name], + [first_name], + [last_name] + ) + VALUES + ( + s.[Id], + s.[email_address], + s.[display_name], + s.[first_name], + s.[last_name] + ) +WHEN MATCHED THEN + UPDATE SET + t.[Id] = s.[Id], + t.[display_name] = s.[display_name], + t.[first_name] = s.[first_name], + t.[last_name] = s.[last_name] +; \ No newline at end of file diff --git a/test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeDataOutputTests.verified.txt b/test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeDataOutputTests.verified.txt new file mode 100644 index 00000000..4c0c5fe7 --- /dev/null +++ b/test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeDataOutputTests.verified.txt @@ -0,0 +1,56 @@ +MERGE INTO [dbo].[User] AS t +USING +( + VALUES + ('test@email.com', 'Test User', 'Test', 'User', NULL, NULL), + ('blah@email.com', 'Blah User', 'Blah', 'User', NULL, NULL), + ('random@email.com', 'Random User', 'Random', 'User', NULL, NULL) +) +AS s +( + [EmailAddress], [DisplayName], [FirstName], [LastName], [LockoutEnd], [LastLogin] +) +ON +( + t.[EmailAddress] = s.[EmailAddress] +) +WHEN NOT MATCHED BY TARGET THEN + INSERT + ( + [EmailAddress], + [DisplayName], + [FirstName], + [LastName], + [LockoutEnd], + [LastLogin] + ) + VALUES + ( + s.[EmailAddress], + s.[DisplayName], + s.[FirstName], + s.[LastName], + s.[LockoutEnd], + s.[LastLogin] + ) +WHEN MATCHED THEN + UPDATE SET + t.[DisplayName] = s.[DisplayName], + t.[FirstName] = s.[FirstName], + t.[LastName] = s.[LastName], + t.[LockoutEnd] = s.[LockoutEnd], + t.[LastLogin] = s.[LastLogin] +OUTPUT + $action as [Action], + DELETED.[EmailAddress] as [OriginalEmailAddress], + INSERTED.[EmailAddress] as [CurrentEmailAddress], + DELETED.[DisplayName] as [OriginalDisplayName], + INSERTED.[DisplayName] as [CurrentDisplayName], + DELETED.[FirstName] as [OriginalFirstName], + INSERTED.[FirstName] as [CurrentFirstName], + DELETED.[LastName] as [OriginalLastName], + INSERTED.[LastName] as [CurrentLastName], + DELETED.[LockoutEnd] as [OriginalLockoutEnd], + INSERTED.[LockoutEnd] as [CurrentLockoutEnd], + DELETED.[LastLogin] as [OriginalLastLogin], + INSERTED.[LastLogin] as [CurrentLastLogin]; \ No newline at end of file diff --git a/test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeDataTests.verified.txt b/test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeDataTests.verified.txt new file mode 100644 index 00000000..b7bba1ae --- /dev/null +++ b/test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeDataTests.verified.txt @@ -0,0 +1,42 @@ +MERGE INTO [dbo].[User] AS t +USING +( + VALUES + ('test@email.com', 'Test User', 'Test', 'User', NULL, NULL), + ('blah@email.com', 'Blah User', 'Blah', 'User', NULL, NULL) +) +AS s +( + [EmailAddress], [DisplayName], [FirstName], [LastName], [LockoutEnd], [LastLogin] +) +ON +( + t.[EmailAddress] = s.[EmailAddress] +) +WHEN NOT MATCHED BY TARGET THEN + INSERT + ( + [EmailAddress], + [DisplayName], + [FirstName], + [LastName], + [LockoutEnd], + [LastLogin] + ) + VALUES + ( + s.[EmailAddress], + s.[DisplayName], + s.[FirstName], + s.[LastName], + s.[LockoutEnd], + s.[LastLogin] + ) +WHEN MATCHED THEN + UPDATE SET + t.[DisplayName] = s.[DisplayName], + t.[FirstName] = s.[FirstName], + t.[LastName] = s.[LastName], + t.[LockoutEnd] = s.[LockoutEnd], + t.[LastLogin] = s.[LastLogin] +; \ No newline at end of file diff --git a/test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeTests.verified.txt b/test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeTests.verified.txt new file mode 100644 index 00000000..96e2e51c --- /dev/null +++ b/test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeTests.verified.txt @@ -0,0 +1,44 @@ +MERGE INTO [dbo].[User] AS t +USING +( + SELECT + [EmailAddress], + [DisplayName], + [FirstName], + [LastName], + [LockoutEnd], + [LastLogin] + FROM [#MergeTable] +) +AS s +ON +( + t.[EmailAddress] = s.[EmailAddress] +) +WHEN NOT MATCHED BY TARGET THEN + INSERT + ( + [EmailAddress], + [DisplayName], + [FirstName], + [LastName], + [LockoutEnd], + [LastLogin] + ) + VALUES + ( + s.[EmailAddress], + s.[DisplayName], + s.[FirstName], + s.[LastName], + s.[LockoutEnd], + s.[LastLogin] + ) +WHEN MATCHED THEN + UPDATE SET + t.[DisplayName] = s.[DisplayName], + t.[FirstName] = s.[FirstName], + t.[LastName] = s.[LastName], + t.[LockoutEnd] = s.[LockoutEnd], + t.[LastLogin] = s.[LastLogin] +; \ No newline at end of file