Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query: Convert SingleResult compared to null to Any #25914

Merged
merged 1 commit into from
Sep 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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