Skip to content

Commit

Permalink
Query: Convert SingleResult compared to null to Any (#25914)
Browse files Browse the repository at this point in the history
Resolves #18476
  • Loading branch information
smitpatel authored Sep 8, 2021
1 parent 1087a73 commit eb30e30
Show file tree
Hide file tree
Showing 10 changed files with 678 additions and 229 deletions.
360 changes: 203 additions & 157 deletions src/EFCore/Query/Internal/QueryOptimizingExpressionVisitor.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2216,6 +2216,90 @@ FROM root c
WHERE ((c[""Discriminator""] = ""Customer"") AND (c[""CustomerID""] = ""ALFKI""))");
}

[ConditionalTheory(Skip = "Issue#17246 (Cross-collection join is not supported)")]
public override Task FirstOrDefault_over_scalar_projection_compared_to_null(bool async)
{
return base.FirstOrDefault_over_scalar_projection_compared_to_null(async);
}

[ConditionalTheory(Skip = "Issue#17246 (Cross-collection join is not supported)")]
public override Task FirstOrDefault_over_scalar_projection_compared_to_not_null(bool async)
{
return base.FirstOrDefault_over_scalar_projection_compared_to_not_null(async);
}

[ConditionalTheory(Skip = "Issue#17246 (Cross-collection join is not supported)")]
public override Task FirstOrDefault_over_custom_projection_compared_to_null(bool async)
{
return base.FirstOrDefault_over_custom_projection_compared_to_null(async);
}

[ConditionalTheory(Skip = "Issue#17246 (Cross-collection join is not supported)")]
public override Task FirstOrDefault_over_custom_projection_compared_to_not_null(bool async)
{
return base.FirstOrDefault_over_custom_projection_compared_to_not_null(async);
}

[ConditionalTheory(Skip = "Issue#17246 (Cross-collection join is not supported)")]
public override Task SingleOrDefault_over_custom_projection_compared_to_null(bool async)
{
return base.SingleOrDefault_over_custom_projection_compared_to_null(async);
}

[ConditionalTheory(Skip = "Issue#17246 (Cross-collection join is not supported)")]
public override Task SingleOrDefault_over_custom_projection_compared_to_not_null(bool async)
{
return base.SingleOrDefault_over_custom_projection_compared_to_not_null(async);
}

[ConditionalTheory(Skip = "Issue#17246 (Cross-collection join is not supported)")]
public override Task LastOrDefault_over_custom_projection_compared_to_null(bool async)
{
return base.LastOrDefault_over_custom_projection_compared_to_null(async);
}

[ConditionalTheory(Skip = "Issue#17246 (Cross-collection join is not supported)")]
public override Task LastOrDefault_over_custom_projection_compared_to_not_null(bool async)
{
return base.LastOrDefault_over_custom_projection_compared_to_not_null(async);
}

[ConditionalTheory(Skip = "Issue#17246 (Cross-collection join is not supported)")]
public override Task First_over_custom_projection_compared_to_null(bool async)
{
return base.First_over_custom_projection_compared_to_null(async);
}

[ConditionalTheory(Skip = "Issue#17246 (Cross-collection join is not supported)")]
public override Task First_over_custom_projection_compared_to_not_null(bool async)
{
return base.First_over_custom_projection_compared_to_not_null(async);
}

[ConditionalTheory(Skip = "Issue#17246 (Cross-collection join is not supported)")]
public override Task Single_over_custom_projection_compared_to_null(bool async)
{
return base.Single_over_custom_projection_compared_to_null(async);
}

[ConditionalTheory(Skip = "Issue#17246 (Cross-collection join is not supported)")]
public override Task Single_over_custom_projection_compared_to_not_null(bool async)
{
return base.Single_over_custom_projection_compared_to_not_null(async);
}

[ConditionalTheory(Skip = "Issue#17246 (Cross-collection join is not supported)")]
public override Task Last_over_custom_projection_compared_to_null(bool async)
{
return base.Last_over_custom_projection_compared_to_null(async);
}

[ConditionalTheory(Skip = "Issue#17246 (Cross-collection join is not supported)")]
public override Task Last_over_custom_projection_compared_to_not_null(bool async)
{
return base.Last_over_custom_projection_compared_to_not_null(async);
}

private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);

Expand Down
150 changes: 149 additions & 1 deletion test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1204,7 +1204,7 @@ public virtual Task Where_select_many_and(bool async)
ss =>
from c in ss.Set<Customer>()
from e in ss.Set<Employee>()
// ReSharper disable ArrangeRedundantParentheses
// ReSharper disable ArrangeRedundantParentheses
#pragma warning disable RCS1032 // Remove redundant parentheses.
where (c.City == "London" && c.Country == "UK")
&& (e.City == "London" && e.Country == "UK")
Expand Down Expand Up @@ -2522,6 +2522,154 @@ public virtual Task Filter_with_EF_Property_using_function_for_property_name(boo
entryCount: 1);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task FirstOrDefault_over_scalar_projection_compared_to_null(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => (int?)o.OrderID).FirstOrDefault() == null),
entryCount: 2);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task FirstOrDefault_over_scalar_projection_compared_to_not_null(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => (int?)o.OrderID).FirstOrDefault() != null),
entryCount: 89);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task FirstOrDefault_over_custom_projection_compared_to_null(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => new { o.OrderID }).FirstOrDefault() == null),
entryCount: 2);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task FirstOrDefault_over_custom_projection_compared_to_not_null(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => new { o.OrderID }).FirstOrDefault() != null),
entryCount: 89);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task SingleOrDefault_over_custom_projection_compared_to_null(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => new { o.OrderID }).SingleOrDefault() == null),
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => new { o.OrderID }).FirstOrDefault() == null),
entryCount: 2);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task SingleOrDefault_over_custom_projection_compared_to_not_null(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => new { o.OrderID }).SingleOrDefault() != null),
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => new { o.OrderID }).FirstOrDefault() != null),
entryCount: 89);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task LastOrDefault_over_custom_projection_compared_to_null(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => new { o.OrderID }).LastOrDefault() == null),
entryCount: 2);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task LastOrDefault_over_custom_projection_compared_to_not_null(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => new { o.OrderID }).LastOrDefault() != null),
entryCount: 89);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task First_over_custom_projection_compared_to_null(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => new { o.OrderID }).First() == null),
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => new { o.OrderID }).FirstOrDefault() == null),
entryCount: 2);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task First_over_custom_projection_compared_to_not_null(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => new { o.OrderID }).First() != null),
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => new { o.OrderID }).FirstOrDefault() != null),
entryCount: 89);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Single_over_custom_projection_compared_to_null(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => new { o.OrderID }).Single() == null),
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => new { o.OrderID }).FirstOrDefault() == null),
entryCount: 2);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Single_over_custom_projection_compared_to_not_null(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => new { o.OrderID }).Single() != null),
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => new { o.OrderID }).FirstOrDefault() != null),
entryCount: 89);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Last_over_custom_projection_compared_to_null(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => new { o.OrderID }).Last() == null),
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => new { o.OrderID }).LastOrDefault() == null),
entryCount: 2);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Last_over_custom_projection_compared_to_not_null(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => new { o.OrderID }).Last() != null),
ss => ss.Set<Customer>().Where(c => c.Orders.Select(o => new { o.OrderID }).LastOrDefault() != null),
entryCount: 89);
}

private string StringMethod(string arg)
=> arg;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3212,11 +3212,10 @@ public override async Task Member_pushdown_with_multiple_collections(bool async)
@"SELECT (
SELECT TOP(1) [l0].[Name]
FROM [LevelThree] AS [l0]
WHERE (
SELECT TOP(1) [l1].[Id]
WHERE EXISTS (
SELECT 1
FROM [LevelTwo] AS [l1]
WHERE [l].[Id] = [l1].[OneToMany_Optional_Inverse2Id]
ORDER BY [l1].[Id]) IS NOT NULL AND (((
WHERE [l].[Id] = [l1].[OneToMany_Optional_Inverse2Id]) AND (((
SELECT TOP(1) [l2].[Id]
FROM [LevelTwo] AS [l2]
WHERE [l].[Id] = [l2].[OneToMany_Optional_Inverse2Id]
Expand Down Expand Up @@ -3701,10 +3700,10 @@ public override async Task Multiple_collection_FirstOrDefault_followed_by_member
@"SELECT [l].[Id], (
SELECT TOP(1) [l0].[Name]
FROM [LevelThree] AS [l0]
WHERE (
SELECT TOP(1) [l1].[Id]
WHERE EXISTS (
SELECT 1
FROM [LevelTwo] AS [l1]
WHERE ([l].[Id] = [l1].[OneToMany_Optional_Inverse2Id]) AND ([l1].[Name] = N'L2 02')) IS NOT NULL AND (((
WHERE ([l].[Id] = [l1].[OneToMany_Optional_Inverse2Id]) AND ([l1].[Name] = N'L2 02')) AND (((
SELECT TOP(1) [l2].[Id]
FROM [LevelTwo] AS [l2]
WHERE ([l].[Id] = [l2].[OneToMany_Optional_Inverse2Id]) AND ([l2].[Name] = N'L2 02')) = [l0].[OneToMany_Optional_Inverse3Id]) OR ((
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5738,11 +5738,10 @@ public override async Task Filter_with_complex_predicate_containing_subquery(boo
AssertSql(
@"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank]
FROM [Gears] AS [g]
WHERE ([g].[FullName] <> N'Dom') AND (
SELECT TOP(1) [w].[Id]
WHERE ([g].[FullName] <> N'Dom') AND EXISTS (
SELECT 1
FROM [Weapons] AS [w]
WHERE ([g].[FullName] = [w].[OwnerFullName]) AND ([w].[IsAutomatic] = CAST(1 AS bit))
ORDER BY [w].[Id]) IS NOT NULL");
WHERE ([g].[FullName] = [w].[OwnerFullName]) AND ([w].[IsAutomatic] = CAST(1 AS bit)))");
}

public override async Task Query_with_complex_let_containing_ordering_and_filter_projecting_firstOrDefault_element_of_let(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -591,11 +591,10 @@ FROM [Customers] AS [c]
OUTER APPLY (
SELECT TOP(1) [o].[OrderID], [o].[ProductID], [o].[Discount], [o].[Quantity], [o].[UnitPrice]
FROM [Order Details] AS [o]
WHERE (
SELECT TOP(1) [o0].[OrderID]
WHERE EXISTS (
SELECT 1
FROM [Orders] AS [o0]
WHERE [c].[CustomerID] = [o0].[CustomerID]
ORDER BY [o0].[OrderID]) IS NOT NULL AND ((
WHERE [c].[CustomerID] = [o0].[CustomerID]) AND ((
SELECT TOP(1) [o1].[OrderID]
FROM [Orders] AS [o1]
WHERE [c].[CustomerID] = [o1].[CustomerID]
Expand All @@ -614,11 +613,10 @@ public override async Task Multiple_collection_navigation_with_FirstOrDefault_ch
@"SELECT (
SELECT TOP(1) [o].[ProductID]
FROM [Order Details] AS [o]
WHERE (
SELECT TOP(1) [o0].[OrderID]
WHERE EXISTS (
SELECT 1
FROM [Orders] AS [o0]
WHERE [c].[CustomerID] = [o0].[CustomerID]
ORDER BY [o0].[OrderID]) IS NOT NULL AND ((
WHERE [c].[CustomerID] = [o0].[CustomerID]) AND ((
SELECT TOP(1) [o1].[OrderID]
FROM [Orders] AS [o1]
WHERE [c].[CustomerID] = [o1].[CustomerID]
Expand Down
Loading

0 comments on commit eb30e30

Please sign in to comment.