Skip to content

Commit ceec5a2

Browse files
Cover predicate counts and the grouping projected alongside an aggregate
- Add Elements_and_predicate_counts: Count and LongCount with a predicate are windowed as COUNT(CASE ...) OVER (...) (COUNT_BIG on SQL Server), with the grouping still carrying every row - Add Grouping_itself_and_aggregate_is_not_translated: projecting the grouping itself alongside an aggregate fails translation the same way with and without the opt-in Part of #12747
1 parent c954d45 commit ceec5a2

3 files changed

Lines changed: 105 additions & 0 deletions

File tree

‎test/EFCore.Relational.Specification.Tests/Query/WindowFunctionQueryRelationalTestBase.cs‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,44 @@ public virtual async Task Elements_and_float_sum()
190190
});
191191
}
192192

193+
/// <summary>
194+
/// Count and LongCount with a predicate: the predicate goes into the aggregate rather than filtering the rows, which the grouping
195+
/// still carries all of.
196+
/// </summary>
197+
[Fact]
198+
public virtual async Task Elements_and_predicate_counts()
199+
{
200+
var contextFactory = await InitializeAsync(useWindowFunctionAggregates: true);
201+
await using var context = contextFactory.CreateDbContext();
202+
203+
var results = await context.Employees
204+
.GroupBy(e => e.DepartmentName)
205+
.Select(
206+
g => new
207+
{
208+
g.Key,
209+
Names = g.Select(e => e.Name).ToList(),
210+
HighEarners = g.Count(e => e.Salary > 100),
211+
LongHighEarners = g.LongCount(e => e.Salary > 100)
212+
})
213+
.ToListAsync();
214+
215+
Assert.Collection(
216+
results.OrderBy(r => r.Key),
217+
engineering =>
218+
{
219+
Assert.Equal(["Alice", "Bob", "Cheng"], engineering.Names.Order());
220+
Assert.Equal(2, engineering.HighEarners);
221+
Assert.Equal(2L, engineering.LongHighEarners);
222+
},
223+
sales =>
224+
{
225+
Assert.Equal(["Dara", "Emre"], sales.Names.Order());
226+
Assert.Equal(0, sales.HighEarners);
227+
Assert.Equal(0L, sales.LongHighEarners);
228+
});
229+
}
230+
193231
[Fact]
194232
public virtual async Task Projected_elements_and_aggregate()
195233
{
@@ -450,6 +488,27 @@ public virtual async Task Elements_without_aggregate()
450488
sales => Assert.Equal(2, sales.Employees.Count));
451489
}
452490

491+
/// <summary>
492+
/// The grouping projected as itself alongside an aggregate has no translation, with or without the opt-in: there are no
493+
/// elements to carry window values on, and exposing the grouping would mean exposing the rows which carry them.
494+
/// </summary>
495+
[Theory, InlineData(false), InlineData(true)]
496+
public virtual async Task Grouping_itself_and_aggregate_is_not_translated(bool useWindowFunctionAggregates)
497+
{
498+
var contextFactory = await InitializeAsync(useWindowFunctionAggregates);
499+
await using var context = contextFactory.CreateDbContext();
500+
501+
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
502+
() => context.Employees
503+
.GroupBy(e => e.DepartmentName)
504+
.Select(g => new { Group = g, Count = g.Count() })
505+
.ToListAsync());
506+
507+
Assert.Contains(
508+
CoreStrings.TranslationFailedWithDetails("", CoreStrings.QuerySelectContainsGrouping)[21..],
509+
exception.Message);
510+
}
511+
453512
/// <summary>
454513
/// COUNT(DISTINCT ...) OVER (...) isn't supported by SQL Server, so a distinct aggregate keeps the GROUP BY translation.
455514
/// </summary>

‎test/EFCore.SqlServer.FunctionalTests/Query/WindowFunctionQuerySqlServerTest.cs‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,22 @@ ORDER BY [e1].[DepartmentName]
8282
""");
8383
}
8484

85+
public override async Task Elements_and_predicate_counts()
86+
{
87+
await base.Elements_and_predicate_counts();
88+
89+
AssertSql(
90+
"""
91+
SELECT [e].[DepartmentName], DENSE_RANK() OVER(ORDER BY [e].[DepartmentName]), [e].[Name], COUNT(CASE
92+
WHEN [e].[Salary] > 100 THEN 1
93+
END) OVER(PARTITION BY [e].[DepartmentName]), COUNT_BIG(CASE
94+
WHEN [e].[Salary] > 100 THEN 1
95+
END) OVER(PARTITION BY [e].[DepartmentName])
96+
FROM [Employees] AS [e]
97+
ORDER BY [e].[DepartmentName]
98+
""");
99+
}
100+
85101
public override async Task Projected_elements_and_aggregate()
86102
{
87103
await base.Projected_elements_and_aggregate();
@@ -212,6 +228,13 @@ ORDER BY [e].[DepartmentName]
212228
""");
213229
}
214230

231+
public override async Task Grouping_itself_and_aggregate_is_not_translated(bool useWindowFunctionAggregates)
232+
{
233+
await base.Grouping_itself_and_aggregate_is_not_translated(useWindowFunctionAggregates);
234+
235+
AssertSql();
236+
}
237+
215238
public override async Task Distinct_aggregate_is_not_lifted()
216239
{
217240
await base.Distinct_aggregate_is_not_lifted();

‎test/EFCore.Sqlite.FunctionalTests/Query/WindowFunctionQuerySqliteTest.cs‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,22 @@ ORDER BY "e1"."DepartmentName"
8282
""");
8383
}
8484

85+
public override async Task Elements_and_predicate_counts()
86+
{
87+
await base.Elements_and_predicate_counts();
88+
89+
AssertSql(
90+
"""
91+
SELECT "e"."DepartmentName", DENSE_RANK() OVER(ORDER BY "e"."DepartmentName"), "e"."Name", COUNT(CASE
92+
WHEN "e"."Salary" > 100 THEN 1
93+
END) OVER(PARTITION BY "e"."DepartmentName"), COUNT(CASE
94+
WHEN "e"."Salary" > 100 THEN 1
95+
END) OVER(PARTITION BY "e"."DepartmentName")
96+
FROM "Employees" AS "e"
97+
ORDER BY "e"."DepartmentName"
98+
""");
99+
}
100+
85101
public override async Task Projected_elements_and_aggregate()
86102
{
87103
await base.Projected_elements_and_aggregate();
@@ -217,6 +233,13 @@ ORDER BY "e"."DepartmentName"
217233
""");
218234
}
219235

236+
public override async Task Grouping_itself_and_aggregate_is_not_translated(bool useWindowFunctionAggregates)
237+
{
238+
await base.Grouping_itself_and_aggregate_is_not_translated(useWindowFunctionAggregates);
239+
240+
AssertSql();
241+
}
242+
220243
public override async Task Distinct_aggregate_is_not_lifted()
221244
{
222245
await base.Distinct_aggregate_is_not_lifted();

0 commit comments

Comments
 (0)