Skip to content

Commit 513f259

Browse files
committed
Fix split query over GroupBy over parameter
Fixes #30022
1 parent 16c1380 commit 513f259

17 files changed

+181
-30
lines changed

src/EFCore.Relational/Properties/RelationalStrings.Designer.cs

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/EFCore.Relational/Properties/RelationalStrings.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -902,6 +902,9 @@
902902
<data name="NoDbCommand" xml:space="preserve">
903903
<value>Cannot create a DbCommand for a non-relational query.</value>
904904
</data>
905+
<data name="NonConstantOrParameterAsInExpressionValues" xml:space="preserve">
906+
<value>Expression of type '{type}' isn't supported as the Values of an InExpression; only constants and parameters are supported.</value>
907+
</data>
905908
<data name="NoneRelationalTypeMappingOnARelationalTypeMappingSource" xml:space="preserve">
906909
<value>'FindMapping' was called on a 'RelationalTypeMappingSource' with a non-relational 'TypeMappingInfo'.</value>
907910
</data>

src/EFCore.Relational/Query/SqlExpressions/SelectExpression.Helper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ when _groupByDiscovery
210210

211211
case SqlExpression sqlExpression
212212
when !_groupByDiscovery
213-
&& sqlExpression is not SqlConstantExpression or SqlParameterExpression
213+
&& sqlExpression is not SqlConstantExpression and not SqlParameterExpression
214214
&& _correlatedTerms.Contains(sqlExpression):
215215
var outerColumn = _subquery.GenerateOuterColumn(_tableReferenceExpression, sqlExpression);
216216
_mappings[sqlExpression] = outerColumn;

src/EFCore.Relational/Query/SqlNullabilityProcessor.cs

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -720,26 +720,28 @@ protected virtual SqlExpression VisitIn(InExpression inExpression, bool allowOpt
720720
{
721721
var inValues = new List<object?>();
722722
var hasNullValue = false;
723-
RelationalTypeMapping? typeMapping = null;
723+
RelationalTypeMapping? typeMapping;
724724

725-
IEnumerable? values = null;
726-
if (valuesExpression is SqlConstantExpression sqlConstant)
725+
IEnumerable values;
726+
switch (valuesExpression)
727727
{
728-
typeMapping = sqlConstant.TypeMapping;
729-
values = (IEnumerable)sqlConstant.Value!;
730-
}
731-
else if (valuesExpression is SqlParameterExpression sqlParameter)
732-
{
733-
DoNotCache();
734-
typeMapping = sqlParameter.TypeMapping;
735-
values = (IEnumerable?)ParameterValues[sqlParameter.Name];
736-
if (values == null)
737-
{
738-
throw new NullReferenceException();
739-
}
728+
case SqlConstantExpression sqlConstant:
729+
typeMapping = sqlConstant.TypeMapping;
730+
values = (IEnumerable)sqlConstant.Value!;
731+
break;
732+
733+
case SqlParameterExpression sqlParameter:
734+
DoNotCache();
735+
typeMapping = sqlParameter.TypeMapping;
736+
values = (IEnumerable?)ParameterValues[sqlParameter.Name] ?? throw new NullReferenceException();
737+
break;
738+
739+
default:
740+
throw new InvalidOperationException(
741+
RelationalStrings.NonConstantOrParameterAsInExpressionValues(valuesExpression.GetType().Name));
740742
}
741743

742-
foreach (var value in values!)
744+
foreach (var value in values)
743745
{
744746
if (value == null && extractNullValues)
745747
{

test/EFCore.Specification.Tests/Query/ComplexNavigationsCollectionsQueryTestBase.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2197,6 +2197,26 @@ public virtual Task Skip_Take_on_grouping_element_inside_collection_projection(b
21972197
});
21982198
});
21992199

2200+
[ConditionalTheory]
2201+
[MemberData(nameof(IsAsyncData))]
2202+
public virtual Task Collection_projection_over_GroupBy_over_parameter(bool async)
2203+
{
2204+
var validIds = new List<string> { "L1 01", "L1 02" };
2205+
2206+
return AssertQuery(
2207+
async,
2208+
ss => ss.Set<Level1>()
2209+
.Where(l1 => validIds.Contains(l1.Name))
2210+
.GroupBy(l => l.Date)
2211+
.Select(g => new { g.Key, Ids = g.Select(e => e.Id) }),
2212+
elementSorter: e => e.Key,
2213+
elementAsserter: (e, a) =>
2214+
{
2215+
AssertEqual(e.Key, a.Key);
2216+
AssertCollection(e.Ids, a.Ids);
2217+
});
2218+
}
2219+
22002220
[ConditionalTheory]
22012221
[MemberData(nameof(IsAsyncData))]
22022222
public virtual Task SelectMany_over_conditional_null_source(bool async)

test/EFCore.Specification.Tests/Query/NorthwindIncludeQueryTestBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1453,7 +1453,7 @@ where i.OrderID < 10800
14531453

14541454
[ConditionalTheory]
14551455
[MemberData(nameof(IsAsyncData))]
1456-
public virtual Task Outer_idenfier_correctly_determined_when_doing_include_on_right_side_of_left_join(bool async)
1456+
public virtual Task Outer_identifier_correctly_determined_when_doing_include_on_right_side_of_left_join(bool async)
14571457
=> AssertQuery(
14581458
async,
14591459
ss => from cust in ss.Set<Customer>()

test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqlServerTest.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2319,6 +2319,28 @@ FROM [LevelOne] AS [l1]
23192319
""");
23202320
}
23212321

2322+
public override async Task Collection_projection_over_GroupBy_over_parameter(bool async)
2323+
{
2324+
await base.Collection_projection_over_GroupBy_over_parameter(async);
2325+
2326+
AssertSql(
2327+
"""
2328+
SELECT [t].[Date], [t0].[Id]
2329+
FROM (
2330+
SELECT [l].[Date]
2331+
FROM [LevelOne] AS [l]
2332+
WHERE [l].[Name] IN (N'L1 01', N'L1 02')
2333+
GROUP BY [l].[Date]
2334+
) AS [t]
2335+
LEFT JOIN (
2336+
SELECT [l0].[Id], [l0].[Date]
2337+
FROM [LevelOne] AS [l0]
2338+
WHERE [l0].[Name] IN (N'L1 01', N'L1 02')
2339+
) AS [t0] ON [t].[Date] = [t0].[Date]
2340+
ORDER BY [t].[Date]
2341+
""");
2342+
}
2343+
23222344
public override async Task Include_partially_added_before_Where_and_then_build_upon(bool async)
23232345
{
23242346
await base.Include_partially_added_before_Where_and_then_build_upon(async);

test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3003,6 +3003,28 @@ FROM [Level1] AS [l1]
30033003
""");
30043004
}
30053005

3006+
public override async Task Collection_projection_over_GroupBy_over_parameter(bool async)
3007+
{
3008+
await base.Collection_projection_over_GroupBy_over_parameter(async);
3009+
3010+
AssertSql(
3011+
"""
3012+
SELECT [t].[Date], [t0].[Id]
3013+
FROM (
3014+
SELECT [l].[Date]
3015+
FROM [Level1] AS [l]
3016+
WHERE [l].[Name] IN (N'L1 01', N'L1 02')
3017+
GROUP BY [l].[Date]
3018+
) AS [t]
3019+
LEFT JOIN (
3020+
SELECT [l0].[Id], [l0].[Date]
3021+
FROM [Level1] AS [l0]
3022+
WHERE [l0].[Name] IN (N'L1 01', N'L1 02')
3023+
) AS [t0] ON [t].[Date] = [t0].[Date]
3024+
ORDER BY [t].[Date]
3025+
""");
3026+
}
3027+
30063028
public override async Task Filtered_include_is_considered_loaded(bool async)
30073029
{
30083030
await base.Filtered_include_is_considered_loaded(async);

test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQuerySqlServerTest.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3728,6 +3728,36 @@ FROM [LevelOne] AS [l1]
37283728
""");
37293729
}
37303730

3731+
public override async Task Collection_projection_over_GroupBy_over_parameter(bool async)
3732+
{
3733+
await base.Collection_projection_over_GroupBy_over_parameter(async);
3734+
3735+
AssertSql(
3736+
"""
3737+
SELECT [l].[Date]
3738+
FROM [LevelOne] AS [l]
3739+
WHERE [l].[Name] IN (N'L1 01', N'L1 02')
3740+
GROUP BY [l].[Date]
3741+
ORDER BY [l].[Date]
3742+
""",
3743+
//
3744+
"""
3745+
SELECT [t0].[Id], [t].[Date]
3746+
FROM (
3747+
SELECT [l].[Date]
3748+
FROM [LevelOne] AS [l]
3749+
WHERE [l].[Name] IN (N'L1 01', N'L1 02')
3750+
GROUP BY [l].[Date]
3751+
) AS [t]
3752+
INNER JOIN (
3753+
SELECT [l0].[Id], [l0].[Date]
3754+
FROM [LevelOne] AS [l0]
3755+
WHERE [l0].[Name] IN (N'L1 01', N'L1 02')
3756+
) AS [t0] ON [t].[Date] = [t0].[Date]
3757+
ORDER BY [t].[Date]
3758+
""");
3759+
}
3760+
37313761
public override async Task Include_partially_added_before_Where_and_then_build_upon(bool async)
37323762
{
37333763
await base.Include_partially_added_before_Where_and_then_build_upon(async);

test/EFCore.SqlServer.FunctionalTests/Query/NorthwindEFPropertyIncludeQuerySqlServerTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,9 +1262,9 @@ FROM [Order Details] AS [o]
12621262
""");
12631263
}
12641264

1265-
public override async Task Outer_idenfier_correctly_determined_when_doing_include_on_right_side_of_left_join(bool async)
1265+
public override async Task Outer_identifier_correctly_determined_when_doing_include_on_right_side_of_left_join(bool async)
12661266
{
1267-
await base.Outer_idenfier_correctly_determined_when_doing_include_on_right_side_of_left_join(async);
1267+
await base.Outer_identifier_correctly_determined_when_doing_include_on_right_side_of_left_join(async);
12681268

12691269
AssertSql(
12701270
"""

0 commit comments

Comments
 (0)