From 8b5fb282bf1643180fa406fdff48dceb4d4b997d Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Wed, 14 Sep 2022 12:34:11 +0100 Subject: [PATCH] Ignore case when comparing store types for set operation validation Partial fix for #29020 --- .../Query/SqlExpressions/SelectExpression.cs | 5 +- .../NorthwindSetOperationsQueryTestBase.cs | 2 +- .../Query/FromSqlQuerySqlServerTest.cs | 2 +- .../NorthwindFunctionsQuerySqlServerTest.cs | 14 ++--- ...NorthwindQueryFiltersQuerySqlServerTest.cs | 54 ++++++++++++------- .../Query/NorthwindQuerySqlServerFixture.cs | 12 +++-- ...orthwindSetOperationsQuerySqlServerTest.cs | 43 +++++++++++++-- 7 files changed, 96 insertions(+), 36 deletions(-) diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs index 2c4152f9a3e..7141c689353 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs @@ -2253,7 +2253,10 @@ private void ApplySetOperation(SetOperationType setOperationType, SelectExpressi var innerColumn2 = (SqlExpression)expression2; // For now, make sure that both sides output the same store type, otherwise the query may fail. // TODO: with #15586 we'll be able to also allow different store types which are implicitly convertible to one another. - if (innerColumn1.TypeMapping!.StoreType != innerColumn2.TypeMapping!.StoreType) + if (!string.Equals( + innerColumn1.TypeMapping!.StoreType, + innerColumn2.TypeMapping!.StoreType, + StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException(RelationalStrings.SetOperationsOnDifferentStoreTypes); } diff --git a/test/EFCore.Specification.Tests/Query/NorthwindSetOperationsQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindSetOperationsQueryTestBase.cs index df1e88d8f12..22fda59679a 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindSetOperationsQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindSetOperationsQueryTestBase.cs @@ -318,7 +318,7 @@ public virtual Task Select_Union_unrelated(bool async) => AssertQuery( async, ss => ss.Set() - .Select(c => c.ContactName) + .Select(c => c.CompanyName) .Union(ss.Set().Select(p => p.ProductName)) .Where(x => x.StartsWith("C")) .OrderBy(x => x), diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/FromSqlQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/FromSqlQuerySqlServerTest.cs index 2e9686a627c..943ebdb0120 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/FromSqlQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/FromSqlQuerySqlServerTest.cs @@ -391,7 +391,7 @@ public override async Task FromSqlRaw_queryable_with_parameters_and_clos AssertSql( @"p0='London' (Size = 4000) -@__contactTitle_1='Sales Representative' (Size = 4000) +@__contactTitle_1='Sales Representative' (Size = 30) SELECT [m].[CustomerID], [m].[Address], [m].[City], [m].[CompanyName], [m].[ContactName], [m].[ContactTitle], [m].[Country], [m].[Fax], [m].[Phone], [m].[PostalCode], [m].[Region] FROM ( diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindFunctionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindFunctionsQuerySqlServerTest.cs index 31fc17cabaf..020dc4e2844 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindFunctionsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindFunctionsQuerySqlServerTest.cs @@ -197,7 +197,7 @@ public override async Task String_Contains_parameter_with_whitespace(bool async) await base.String_Contains_parameter_with_whitespace(async); AssertSql( - @"@__pattern_0=' ' (Size = 4000) + @"@__pattern_0=' ' (Size = 30) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] @@ -1691,7 +1691,7 @@ public override async Task Indexof_with_one_constant_arg(bool async) AssertSql( @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE (CAST(CHARINDEX(N'a', [c].[ContactName]) AS int) - 1) = 1"); +WHERE (CHARINDEX(N'a', [c].[ContactName]) - 1) = 1"); } public override async Task Indexof_with_one_parameter_arg(bool async) @@ -1699,13 +1699,13 @@ public override async Task Indexof_with_one_parameter_arg(bool async) await base.Indexof_with_one_parameter_arg(async); AssertSql( - @"@__pattern_0='a' (Size = 4000) + @"@__pattern_0='a' (Size = 30) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] WHERE CASE WHEN @__pattern_0 = N'' THEN 0 - ELSE CAST(CHARINDEX(@__pattern_0, [c].[ContactName]) AS int) - 1 + ELSE CHARINDEX(@__pattern_0, [c].[ContactName]) - 1 END = 1"); } @@ -1716,7 +1716,7 @@ public override async Task Indexof_with_constant_starting_position(bool async) AssertSql( @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE (CAST(CHARINDEX(N'a', [c].[ContactName], 3) AS int) - 1) = 4"); +WHERE (CHARINDEX(N'a', [c].[ContactName], 3) - 1) = 4"); } public override async Task Indexof_with_parameter_starting_position(bool async) @@ -1728,7 +1728,7 @@ public override async Task Indexof_with_parameter_starting_position(bool async) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE (CAST(CHARINDEX(N'a', [c].[ContactName], @__start_0 + 1) AS int) - 1) = 4"); +WHERE (CHARINDEX(N'a', [c].[ContactName], @__start_0 + 1) - 1) = 4"); } public override async Task Replace_with_emptystring(bool async) @@ -1830,7 +1830,7 @@ public override async Task Substring_with_two_args_with_Index_of(bool async) await base.Substring_with_two_args_with_Index_of(async); AssertSql( - @"SELECT SUBSTRING([c].[ContactName], (CAST(CHARINDEX(N'a', [c].[ContactName]) AS int) - 1) + 1, 3) + @"SELECT SUBSTRING([c].[ContactName], (CHARINDEX(N'a', [c].[ContactName]) - 1) + 1, 3) FROM [Customers] AS [c] WHERE [c].[CustomerID] = N'ALFKI'"); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindQueryFiltersQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindQueryFiltersQuerySqlServerTest.cs index b03ec35f6c4..d30787f9b37 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindQueryFiltersQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindQueryFiltersQuerySqlServerTest.cs @@ -27,10 +27,11 @@ public override async Task Count_query(bool async) AssertSql( @"@__ef_filter__TenantPrefix_0='B' (Size = 4000) +@__ef_filter__TenantPrefix_0_1='B' (Size = 40) SELECT COUNT(*) FROM [Customers] AS [c] -WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)"); +WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0_1)) = @__ef_filter__TenantPrefix_0)"); } public override async Task Materialized_query(bool async) @@ -39,10 +40,11 @@ public override async Task Materialized_query(bool async) AssertSql( @"@__ef_filter__TenantPrefix_0='B' (Size = 4000) +@__ef_filter__TenantPrefix_0_1='B' (Size = 40) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)"); +WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0_1)) = @__ef_filter__TenantPrefix_0)"); } public override async Task Find(bool async) @@ -51,11 +53,12 @@ public override async Task Find(bool async) AssertSql( @"@__ef_filter__TenantPrefix_0='B' (Size = 4000) +@__ef_filter__TenantPrefix_0_1='B' (Size = 40) @__p_0='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT TOP(1) [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE (@__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)) AND [c].[CustomerID] = @__p_0"); +WHERE (@__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0_1)) = @__ef_filter__TenantPrefix_0)) AND [c].[CustomerID] = @__p_0"); } public override async Task Materialized_query_parameter(bool async) @@ -64,10 +67,11 @@ public override async Task Materialized_query_parameter(bool async) AssertSql( @"@__ef_filter__TenantPrefix_0='F' (Size = 4000) +@__ef_filter__TenantPrefix_0_1='F' (Size = 40) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)"); +WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0_1)) = @__ef_filter__TenantPrefix_0)"); } public override async Task Materialized_query_parameter_new_context(bool async) @@ -76,16 +80,18 @@ public override async Task Materialized_query_parameter_new_context(bool async) AssertSql( @"@__ef_filter__TenantPrefix_0='B' (Size = 4000) +@__ef_filter__TenantPrefix_0_1='B' (Size = 40) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)", +WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0_1)) = @__ef_filter__TenantPrefix_0)", // @"@__ef_filter__TenantPrefix_0='T' (Size = 4000) +@__ef_filter__TenantPrefix_0_1='T' (Size = 40) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)"); +WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0_1)) = @__ef_filter__TenantPrefix_0)"); } public override async Task Projection_query_parameter(bool async) @@ -94,10 +100,11 @@ public override async Task Projection_query_parameter(bool async) AssertSql( @"@__ef_filter__TenantPrefix_0='F' (Size = 4000) +@__ef_filter__TenantPrefix_0_1='F' (Size = 40) SELECT [c].[CustomerID] FROM [Customers] AS [c] -WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)"); +WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0_1)) = @__ef_filter__TenantPrefix_0)"); } public override async Task Projection_query(bool async) @@ -106,10 +113,11 @@ public override async Task Projection_query(bool async) AssertSql( @"@__ef_filter__TenantPrefix_0='B' (Size = 4000) +@__ef_filter__TenantPrefix_0_1='B' (Size = 40) SELECT [c].[CustomerID] FROM [Customers] AS [c] -WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)"); +WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0_1)) = @__ef_filter__TenantPrefix_0)"); } public override async Task Include_query(bool async) @@ -118,6 +126,7 @@ public override async Task Include_query(bool async) AssertSql( @"@__ef_filter__TenantPrefix_0='B' (Size = 4000) +@__ef_filter__TenantPrefix_0_1='B' (Size = 40) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t0].[CustomerID0] FROM [Customers] AS [c] @@ -127,7 +136,7 @@ FROM [Orders] AS [o] LEFT JOIN ( SELECT [c0].[CustomerID], [c0].[CompanyName] FROM [Customers] AS [c0] - WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c0].[CompanyName] IS NOT NULL AND LEFT([c0].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0) + WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c0].[CompanyName] IS NOT NULL AND LEFT([c0].[CompanyName], LEN(@__ef_filter__TenantPrefix_0_1)) = @__ef_filter__TenantPrefix_0) ) AS [t] ON [o].[CustomerID] = [t].[CustomerID] WHERE [t].[CustomerID] IS NOT NULL AND [t].[CompanyName] IS NOT NULL ) AS [t0] ON [c].[CustomerID] = [t0].[CustomerID] @@ -152,13 +161,14 @@ public override async Task Included_many_to_one_query(bool async) AssertSql( @"@__ef_filter__TenantPrefix_0='B' (Size = 4000) +@__ef_filter__TenantPrefix_0_1='B' (Size = 40) SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region] FROM [Orders] AS [o] LEFT JOIN ( SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] - WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0) + WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0_1)) = @__ef_filter__TenantPrefix_0) ) AS [t] ON [o].[CustomerID] = [t].[CustomerID] WHERE [t].[CustomerID] IS NOT NULL AND [t].[CompanyName] IS NOT NULL"); } @@ -169,6 +179,7 @@ public override async Task Project_reference_that_itself_has_query_filter_with_a AssertSql( @"@__ef_filter__TenantPrefix_1='B' (Size = 4000) +@__ef_filter__TenantPrefix_1_1='B' (Size = 40) @__ef_filter___quantity_0='50' SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate] @@ -179,7 +190,7 @@ FROM [Orders] AS [o0] LEFT JOIN ( SELECT [c].[CustomerID], [c].[CompanyName] FROM [Customers] AS [c] - WHERE @__ef_filter__TenantPrefix_1 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_1)) = @__ef_filter__TenantPrefix_1) + WHERE @__ef_filter__TenantPrefix_1 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_1_1)) = @__ef_filter__TenantPrefix_1) ) AS [t] ON [o0].[CustomerID] = [t].[CustomerID] WHERE [t].[CustomerID] IS NOT NULL AND [t].[CompanyName] IS NOT NULL ) AS [t0] ON [o].[OrderID] = [t0].[OrderID] @@ -192,6 +203,7 @@ public override async Task Navs_query(bool async) AssertSql( @"@__ef_filter__TenantPrefix_0='B' (Size = 4000) +@__ef_filter__TenantPrefix_0_1='B' (Size = 40) @__ef_filter___quantity_1='50' SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] @@ -202,7 +214,7 @@ FROM [Orders] AS [o] LEFT JOIN ( SELECT [c0].[CustomerID], [c0].[CompanyName] FROM [Customers] AS [c0] - WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c0].[CompanyName] IS NOT NULL AND LEFT([c0].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0) + WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c0].[CompanyName] IS NOT NULL AND LEFT([c0].[CompanyName], LEN(@__ef_filter__TenantPrefix_0_1)) = @__ef_filter__TenantPrefix_0) ) AS [t] ON [o].[CustomerID] = [t].[CustomerID] WHERE [t].[CustomerID] IS NOT NULL AND [t].[CompanyName] IS NOT NULL ) AS [t0] ON [c].[CustomerID] = [t0].[CustomerID] @@ -236,12 +248,13 @@ public void FromSql_is_composed() AssertSql( @"@__ef_filter__TenantPrefix_0='B' (Size = 4000) +@__ef_filter__TenantPrefix_0_1='B' (Size = 40) SELECT [m].[CustomerID], [m].[Address], [m].[City], [m].[CompanyName], [m].[ContactName], [m].[ContactTitle], [m].[Country], [m].[Fax], [m].[Phone], [m].[PostalCode], [m].[Region] FROM ( select * from Customers ) AS [m] -WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([m].[CompanyName] IS NOT NULL AND LEFT([m].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)"); +WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([m].[CompanyName] IS NOT NULL AND LEFT([m].[CompanyName], LEN(@__ef_filter__TenantPrefix_0_1)) = @__ef_filter__TenantPrefix_0)"); } [ConditionalFact] @@ -256,6 +269,7 @@ public void FromSql_is_composed_when_filter_has_navigation() AssertSql( @"@__ef_filter__TenantPrefix_0='B' (Size = 4000) +@__ef_filter__TenantPrefix_0_1='B' (Size = 40) SELECT [m].[OrderID], [m].[CustomerID], [m].[EmployeeID], [m].[OrderDate] FROM ( @@ -264,7 +278,7 @@ public void FromSql_is_composed_when_filter_has_navigation() LEFT JOIN ( SELECT [c].[CustomerID], [c].[CompanyName] FROM [Customers] AS [c] - WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0) + WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0_1)) = @__ef_filter__TenantPrefix_0) ) AS [t] ON [m].[CustomerID] = [t].[CustomerID] WHERE [t].[CustomerID] IS NOT NULL AND [t].[CompanyName] IS NOT NULL"); } @@ -275,18 +289,20 @@ public override void Compiled_query() AssertSql( @"@__ef_filter__TenantPrefix_0='B' (Size = 4000) +@__ef_filter__TenantPrefix_0_1='B' (Size = 40) @__customerID='BERGS' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE (@__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)) AND [c].[CustomerID] = @__customerID", +WHERE (@__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0_1)) = @__ef_filter__TenantPrefix_0)) AND [c].[CustomerID] = @__customerID", // @"@__ef_filter__TenantPrefix_0='B' (Size = 4000) +@__ef_filter__TenantPrefix_0_1='B' (Size = 40) @__customerID='BLAUS' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE (@__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)) AND [c].[CustomerID] = @__customerID"); +WHERE (@__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0_1)) = @__ef_filter__TenantPrefix_0)) AND [c].[CustomerID] = @__customerID"); } public override async Task Entity_Equality(bool async) @@ -295,13 +311,14 @@ public override async Task Entity_Equality(bool async) AssertSql( @"@__ef_filter__TenantPrefix_0='B' (Size = 4000) +@__ef_filter__TenantPrefix_0_1='B' (Size = 40) SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM [Orders] AS [o] LEFT JOIN ( SELECT [c].[CustomerID], [c].[CompanyName] FROM [Customers] AS [c] - WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0) + WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0_1)) = @__ef_filter__TenantPrefix_0) ) AS [t] ON [o].[CustomerID] = [t].[CustomerID] WHERE [t].[CustomerID] IS NOT NULL AND [t].[CompanyName] IS NOT NULL"); } @@ -319,13 +336,14 @@ public override async Task Included_many_to_one_query2(bool async) AssertSql( @"@__ef_filter__TenantPrefix_0='B' (Size = 4000) +@__ef_filter__TenantPrefix_0_1='B' (Size = 40) SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region] FROM [Orders] AS [o] LEFT JOIN ( SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] - WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0) + WHERE @__ef_filter__TenantPrefix_0 = N'' OR ([c].[CompanyName] IS NOT NULL AND LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0_1)) = @__ef_filter__TenantPrefix_0) ) AS [t] ON [o].[CustomerID] = [t].[CustomerID] WHERE [t].[CustomerID] IS NOT NULL AND [t].[CompanyName] IS NOT NULL"); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindQuerySqlServerFixture.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindQuerySqlServerFixture.cs index 610902009e8..fc42998b4ad 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindQuerySqlServerFixture.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindQuerySqlServerFixture.cs @@ -15,9 +15,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con { base.OnModelCreating(modelBuilder, context); - modelBuilder.Entity() - .Property(c => c.CustomerID) - .HasColumnType("nchar(5)"); + modelBuilder.Entity( + b => + { + b.Property(c => c.CustomerID).HasColumnType("nchar(5)"); + b.Property(cm => cm.CompanyName).HasMaxLength(40); + b.Property(cm => cm.ContactName).HasMaxLength(30); + b.Property(cm => cm.ContactTitle).HasColumnType("NVarChar(30)"); + }); modelBuilder.Entity( b => @@ -42,6 +47,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con { b.Property(p => p.UnitPrice).HasColumnType("money"); b.Property(p => p.UnitsInStock).HasColumnType("smallint"); + b.Property(cm => cm.ProductName).HasMaxLength(40); }); modelBuilder.Entity() diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSetOperationsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSetOperationsQuerySqlServerTest.cs index 54f2075b0dd..93cc79e9f7e 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSetOperationsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSetOperationsQuerySqlServerTest.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.EntityFrameworkCore.TestModels.Northwind; + namespace Microsoft.EntityFrameworkCore.Query; public class NorthwindSetOperationsQuerySqlServerTest : NorthwindSetOperationsQueryRelationalTestBase< @@ -284,16 +286,16 @@ public override async Task Select_Union_unrelated(bool async) await base.Select_Union_unrelated(async); AssertSql( - @"SELECT [t].[ContactName] + @"SELECT [t].[CompanyName] FROM ( - SELECT [c].[ContactName] + SELECT [c].[CompanyName] FROM [Customers] AS [c] UNION - SELECT [p].[ProductName] AS [ContactName] + SELECT [p].[ProductName] AS [CompanyName] FROM [Products] AS [p] ) AS [t] -WHERE [t].[ContactName] IS NOT NULL AND ([t].[ContactName] LIKE N'C%') -ORDER BY [t].[ContactName]"); +WHERE [t].[CompanyName] IS NOT NULL AND ([t].[CompanyName] LIKE N'C%') +ORDER BY [t].[CompanyName]"); } public override async Task Select_Union_different_fields_in_anonymous_with_subquery(bool async) @@ -1182,6 +1184,37 @@ public override async Task Client_eval_Union_FirstOrDefault(bool async) AssertSql(); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Union_with_different_store_types_throws(bool async) + { + AssertEqual( + RelationalStrings.SetOperationsOnDifferentStoreTypes, + (await Assert.ThrowsAsync( + () => AssertQuery( + async, + ss => ss.Set() + .Select(e => e.CompanyName) + .Union(ss.Set().Select(e => e.ContactName))))).Message); + } + + [ConditionalTheory] // Issue #29020 + [MemberData(nameof(IsAsyncData))] + public virtual async Task Union_with_store_types_differing_only_by_case(bool async) + { + await AssertQuery( + async, + ss => ss.Set() + .Select(e => e.ContactName) + .Union(ss.Set().Select(e => e.ContactTitle))); + + AssertSql(@"SELECT [c].[ContactName] +FROM [Customers] AS [c] +UNION +SELECT [c0].[ContactTitle] AS [ContactName] +FROM [Customers] AS [c0]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected);