Skip to content

Commit

Permalink
Query: Add support for set operations between 2 collection navs
Browse files Browse the repository at this point in the history
Resolves #17759
  • Loading branch information
smitpatel committed Nov 6, 2020
1 parent 0032742 commit 2921617
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ when QueryableMethods.IsSumWithSelector(method):
when genericMethod == QueryableMethods.Join:
{
var secondArgument = Visit(methodCallExpression.Arguments[1]);
secondArgument = UnwrapCollectionMaterialization(secondArgument);
if (secondArgument is NavigationExpansionExpression innerSource)
{
return ProcessJoin(
Expand All @@ -399,6 +400,7 @@ when QueryableMethods.IsSumWithSelector(method):
when genericMethod == QueryableExtensions.LeftJoinMethodInfo:
{
var secondArgument = Visit(methodCallExpression.Arguments[1]);
secondArgument = UnwrapCollectionMaterialization(secondArgument);
if (secondArgument is NavigationExpansionExpression innerSource)
{
return ProcessLeftJoin(
Expand Down Expand Up @@ -436,6 +438,7 @@ when QueryableMethods.IsSumWithSelector(method):
when genericMethod == QueryableMethods.Union:
{
var secondArgument = Visit(methodCallExpression.Arguments[1]);
secondArgument = UnwrapCollectionMaterialization(secondArgument);
if (secondArgument is NavigationExpansionExpression innerSource)
{
return ProcessSetOperation(source, genericMethod, innerSource);
Expand Down Expand Up @@ -1130,10 +1133,7 @@ private NavigationExpansionExpression ProcessSelectMany(
LambdaExpression? resultSelector)
{
var collectionSelectorBody = ExpandNavigationsForSource(source, RemapLambdaExpression(source, collectionSelector));
if (collectionSelectorBody is MaterializeCollectionNavigationExpression materializeCollectionNavigationExpression)
{
collectionSelectorBody = materializeCollectionNavigationExpression.Subquery;
}
collectionSelectorBody = UnwrapCollectionMaterialization(collectionSelectorBody);

if (collectionSelectorBody is NavigationExpansionExpression collectionSource)
{
Expand Down
26 changes: 25 additions & 1 deletion test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1487,7 +1487,7 @@ public virtual Task Where_subquery_distinct_orderby_firstordefault_boolean_with_
ss => ss.Set<Gear>().Where(g => g.HasSoulPatch && g.Weapons.Distinct().OrderBy(w => w.Id).FirstOrDefault().IsAutomatic));
}

[ConditionalTheory(Skip = "Issue#17759")]
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Where_subquery_union_firstordefault_boolean(bool async)
{
Expand All @@ -1497,6 +1497,30 @@ public virtual Task Where_subquery_union_firstordefault_boolean(bool async)
g => g.HasSoulPatch && g.Weapons.Union(g.Weapons).OrderBy(w => w.Id).FirstOrDefault().IsAutomatic));
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Where_subquery_join_firstordefault_boolean(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Gear>().Where(
g => g.HasSoulPatch && g.Weapons.Join(g.Weapons, e => e.Id, e => e.Id, (e1, e2) => e1).OrderBy(w => w.Id).FirstOrDefault().IsAutomatic));
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Where_subquery_left_join_firstordefault_boolean(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Gear>().Where(
g => g.HasSoulPatch
&& (from o in g.Weapons
join i in g.Weapons on o.Id equals i.Id into grouping
from i in grouping.DefaultIfEmpty()
select o).OrderBy(w => w.Id).FirstOrDefault().IsAutomatic));
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Where_subquery_concat_firstordefault_boolean(bool async)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1488,31 +1488,56 @@ public override async Task Where_subquery_union_firstordefault_boolean(bool asyn
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].[HasSoulPatch] = 1",
//
@"@_outer_FullName6='Damon Baird' (Size = 450)
WHERE ([g].[HasSoulPatch] = CAST(1 AS bit)) AND ((
SELECT TOP(1) [t].[IsAutomatic]
FROM (
SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId]
FROM [Weapons] AS [w]
WHERE [g].[FullName] = [w].[OwnerFullName]
UNION
SELECT [w0].[Id], [w0].[AmmunitionType], [w0].[IsAutomatic], [w0].[Name], [w0].[OwnerFullName], [w0].[SynergyWithId]
FROM [Weapons] AS [w0]
WHERE [g].[FullName] = [w0].[OwnerFullName]
) AS [t]
ORDER BY [t].[Id]) = CAST(1 AS bit))");
}

SELECT [w6].[Id], [w6].[AmmunitionType], [w6].[IsAutomatic], [w6].[Name], [w6].[OwnerFullName], [w6].[SynergyWithId]
FROM [Weapons] AS [w6]
WHERE @_outer_FullName6 = [w6].[OwnerFullName]",
//
@"@_outer_FullName5='Damon Baird' (Size = 450)
public override async Task Where_subquery_join_firstordefault_boolean(bool async)
{
await base.Where_subquery_join_firstordefault_boolean(async);

SELECT [w5].[Id], [w5].[AmmunitionType], [w5].[IsAutomatic], [w5].[Name], [w5].[OwnerFullName], [w5].[SynergyWithId]
FROM [Weapons] AS [w5]
WHERE @_outer_FullName5 = [w5].[OwnerFullName]",
//
@"@_outer_FullName6='Marcus Fenix' (Size = 450)
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].[HasSoulPatch] = CAST(1 AS bit)) AND ((
SELECT TOP(1) [w].[IsAutomatic]
FROM [Weapons] AS [w]
INNER JOIN (
SELECT [w0].[Id], [w0].[AmmunitionType], [w0].[IsAutomatic], [w0].[Name], [w0].[OwnerFullName], [w0].[SynergyWithId]
FROM [Weapons] AS [w0]
WHERE [g].[FullName] = [w0].[OwnerFullName]
) AS [t] ON [w].[Id] = [t].[Id]
WHERE [g].[FullName] = [w].[OwnerFullName]
ORDER BY [w].[Id]) = CAST(1 AS bit))");
}

SELECT [w6].[Id], [w6].[AmmunitionType], [w6].[IsAutomatic], [w6].[Name], [w6].[OwnerFullName], [w6].[SynergyWithId]
FROM [Weapons] AS [w6]
WHERE @_outer_FullName6 = [w6].[OwnerFullName]",
//
@"@_outer_FullName5='Marcus Fenix' (Size = 450)
public override async Task Where_subquery_left_join_firstordefault_boolean(bool async)
{
await base.Where_subquery_left_join_firstordefault_boolean(async);

SELECT [w5].[Id], [w5].[AmmunitionType], [w5].[IsAutomatic], [w5].[Name], [w5].[OwnerFullName], [w5].[SynergyWithId]
FROM [Weapons] AS [w5]
WHERE @_outer_FullName5 = [w5].[OwnerFullName]");
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].[HasSoulPatch] = CAST(1 AS bit)) AND ((
SELECT TOP(1) [w].[IsAutomatic]
FROM [Weapons] AS [w]
LEFT JOIN (
SELECT [w0].[Id], [w0].[AmmunitionType], [w0].[IsAutomatic], [w0].[Name], [w0].[OwnerFullName], [w0].[SynergyWithId]
FROM [Weapons] AS [w0]
WHERE [g].[FullName] = [w0].[OwnerFullName]
) AS [t] ON [w].[Id] = [t].[Id]
WHERE [g].[FullName] = [w].[OwnerFullName]
ORDER BY [w].[Id]) = CAST(1 AS bit))");
}

public override async Task Concat_with_count(bool async)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1799,33 +1799,67 @@ public override async Task Where_subquery_union_firstordefault_boolean(bool asyn
await base.Where_subquery_union_firstordefault_boolean(async);

AssertSql(
@"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank]
@"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE
WHEN [o].[Nickname] IS NOT NULL THEN N'Officer'
END AS [Discriminator]
FROM [Gears] AS [g]
WHERE [g].[HasSoulPatch] = 1",
//
@"@_outer_FullName6='Damon Baird' (Size = 450)
LEFT JOIN [Officers] AS [o] ON ([g].[Nickname] = [o].[Nickname]) AND ([g].[SquadId] = [o].[SquadId])
WHERE ([g].[HasSoulPatch] = CAST(1 AS bit)) AND ((
SELECT TOP(1) [t].[IsAutomatic]
FROM (
SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId]
FROM [Weapons] AS [w]
WHERE [g].[FullName] = [w].[OwnerFullName]
UNION
SELECT [w0].[Id], [w0].[AmmunitionType], [w0].[IsAutomatic], [w0].[Name], [w0].[OwnerFullName], [w0].[SynergyWithId]
FROM [Weapons] AS [w0]
WHERE [g].[FullName] = [w0].[OwnerFullName]
) AS [t]
ORDER BY [t].[Id]) = CAST(1 AS bit))");
}

SELECT [w6].[Id], [w6].[AmmunitionType], [w6].[IsAutomatic], [w6].[Name], [w6].[OwnerFullName], [w6].[SynergyWithId]
FROM [Weapons] AS [w6]
WHERE @_outer_FullName6 = [w6].[OwnerFullName]",
//
@"@_outer_FullName5='Damon Baird' (Size = 450)
public override async Task Where_subquery_join_firstordefault_boolean(bool async)
{
await base.Where_subquery_join_firstordefault_boolean(async);

SELECT [w5].[Id], [w5].[AmmunitionType], [w5].[IsAutomatic], [w5].[Name], [w5].[OwnerFullName], [w5].[SynergyWithId]
FROM [Weapons] AS [w5]
WHERE @_outer_FullName5 = [w5].[OwnerFullName]",
//
@"@_outer_FullName6='Marcus Fenix' (Size = 450)
AssertSql(
@"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE
WHEN [o].[Nickname] IS NOT NULL THEN N'Officer'
END AS [Discriminator]
FROM [Gears] AS [g]
LEFT JOIN [Officers] AS [o] ON ([g].[Nickname] = [o].[Nickname]) AND ([g].[SquadId] = [o].[SquadId])
WHERE ([g].[HasSoulPatch] = CAST(1 AS bit)) AND ((
SELECT TOP(1) [w].[IsAutomatic]
FROM [Weapons] AS [w]
INNER JOIN (
SELECT [w0].[Id], [w0].[AmmunitionType], [w0].[IsAutomatic], [w0].[Name], [w0].[OwnerFullName], [w0].[SynergyWithId]
FROM [Weapons] AS [w0]
WHERE [g].[FullName] = [w0].[OwnerFullName]
) AS [t] ON [w].[Id] = [t].[Id]
WHERE [g].[FullName] = [w].[OwnerFullName]
ORDER BY [w].[Id]) = CAST(1 AS bit))");
}

SELECT [w6].[Id], [w6].[AmmunitionType], [w6].[IsAutomatic], [w6].[Name], [w6].[OwnerFullName], [w6].[SynergyWithId]
FROM [Weapons] AS [w6]
WHERE @_outer_FullName6 = [w6].[OwnerFullName]",
//
@"@_outer_FullName5='Marcus Fenix' (Size = 450)
public override async Task Where_subquery_left_join_firstordefault_boolean(bool async)
{
await base.Where_subquery_left_join_firstordefault_boolean(async);

SELECT [w5].[Id], [w5].[AmmunitionType], [w5].[IsAutomatic], [w5].[Name], [w5].[OwnerFullName], [w5].[SynergyWithId]
FROM [Weapons] AS [w5]
WHERE @_outer_FullName5 = [w5].[OwnerFullName]");
AssertSql(
@"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE
WHEN [o].[Nickname] IS NOT NULL THEN N'Officer'
END AS [Discriminator]
FROM [Gears] AS [g]
LEFT JOIN [Officers] AS [o] ON ([g].[Nickname] = [o].[Nickname]) AND ([g].[SquadId] = [o].[SquadId])
WHERE ([g].[HasSoulPatch] = CAST(1 AS bit)) AND ((
SELECT TOP(1) [w].[IsAutomatic]
FROM [Weapons] AS [w]
LEFT JOIN (
SELECT [w0].[Id], [w0].[AmmunitionType], [w0].[IsAutomatic], [w0].[Name], [w0].[OwnerFullName], [w0].[SynergyWithId]
FROM [Weapons] AS [w0]
WHERE [g].[FullName] = [w0].[OwnerFullName]
) AS [t] ON [w].[Id] = [t].[Id]
WHERE [g].[FullName] = [w].[OwnerFullName]
ORDER BY [w].[Id]) = CAST(1 AS bit))");
}

public override async Task Concat_with_count(bool async)
Expand Down

0 comments on commit 2921617

Please sign in to comment.