Skip to content

Commit a594e2f

Browse files
committed
DISTINCT AND TOP can't be in the same stateement with Jet. Push DISTINCT into a subquery
1 parent 2281916 commit a594e2f

File tree

4 files changed

+130
-90
lines changed

4 files changed

+130
-90
lines changed

src/EFCore.Jet/Query/Internal/JetQueryableMethodTranslatingExpressionVisitor.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,13 @@ protected override QueryableMethodTranslatingExpressionVisitor CreateSubqueryVis
150150
}*/
151151

152152
}
153+
154+
//With Jet DISTINCT and TOP can't be used together in the same statement
155+
//Make the DISTINCT into a subquery
156+
if (selectExpression.IsDistinct)
157+
{
158+
selectExpression.PushdownIntoSubquery();
159+
}
153160
return base.TranslateTake(source, count);
154161
}
155162

test/EFCore.Jet.FunctionalTests/Query/NorthwindMiscellaneousQueryJetTest.cs

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2126,13 +2126,16 @@ public override async Task Distinct_Take_Count(bool isAsync)
21262126
await base.Distinct_Take_Count(isAsync);
21272127

21282128
AssertSql(
2129-
$@"{AssertSqlHelper.Declaration("@__p_0='5'")}
2130-
2129+
"""
21312130
SELECT COUNT(*)
21322131
FROM (
2133-
SELECT DISTINCT TOP {AssertSqlHelper.Parameter("@__p_0")} `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
2134-
FROM `Orders` AS `o`
2135-
) AS `t`");
2132+
SELECT TOP 5 `t`.`OrderID`
2133+
FROM (
2134+
SELECT DISTINCT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
2135+
FROM `Orders` AS `o`
2136+
) AS `t`
2137+
) AS `t0`
2138+
""");
21362139
}
21372140

21382141
public override async Task OrderBy_shadow(bool isAsync)
@@ -3348,17 +3351,21 @@ public override async Task OrderBy_coalesce_skip_take_distinct_take(bool isAsync
33483351
await base.OrderBy_coalesce_skip_take_distinct_take(isAsync);
33493352

33503353
AssertSql(
3351-
$@"{AssertSqlHelper.Declaration("@__p_0='5'")}
3352-
3353-
{AssertSqlHelper.Declaration("@__p_1='15'")}
3354-
3355-
SELECT DISTINCT TOP {AssertSqlHelper.Parameter("@__p_0")} `t`.`ProductID`, `t`.`Discontinued`, `t`.`ProductName`, `t`.`SupplierID`, `t`.`UnitPrice`, `t`.`UnitsInStock`
3354+
"""
3355+
SELECT TOP 5 `t1`.`ProductID`, `t1`.`Discontinued`, `t1`.`ProductName`, `t1`.`SupplierID`, `t1`.`UnitPrice`, `t1`.`UnitsInStock`
33563356
FROM (
3357-
SELECT `p`.`ProductID`, `p`.`Discontinued`, `p`.`ProductName`, `p`.`SupplierID`, `p`.`UnitPrice`, `p`.`UnitsInStock`, IIF(`p`.`UnitPrice` IS NULL, NULL, `p`.`UnitPrice`) AS `c`
3358-
FROM `Products` AS `p`
3359-
ORDER BY IIF(`p`.`UnitPrice` IS NULL, NULL, `p`.`UnitPrice`)
3360-
SKIP {AssertSqlHelper.Parameter("@__p_0")} FETCH NEXT {AssertSqlHelper.Parameter("@__p_1")} ROWS ONLY
3361-
) AS `t`");
3357+
SELECT DISTINCT `t0`.`ProductID`, `t0`.`Discontinued`, `t0`.`ProductName`, `t0`.`SupplierID`, `t0`.`UnitPrice`, `t0`.`UnitsInStock`
3358+
FROM (
3359+
SELECT TOP 15 `t`.`ProductID`, `t`.`Discontinued`, `t`.`ProductName`, `t`.`SupplierID`, `t`.`UnitPrice`, `t`.`UnitsInStock`
3360+
FROM (
3361+
SELECT TOP 20 `p`.`ProductID`, `p`.`Discontinued`, `p`.`ProductName`, `p`.`SupplierID`, `p`.`UnitPrice`, `p`.`UnitsInStock`, IIF(`p`.`UnitPrice` IS NULL, 0.0, `p`.`UnitPrice`) AS `c`
3362+
FROM `Products` AS `p`
3363+
ORDER BY IIF(`p`.`UnitPrice` IS NULL, 0.0, `p`.`UnitPrice`)
3364+
) AS `t`
3365+
ORDER BY `t`.`c` DESC
3366+
) AS `t0`
3367+
) AS `t1`
3368+
""");
33623369
}
33633370

33643371
public override async Task OrderBy_skip_take_distinct_orderby_take(bool isAsync)

test/EFCore.Jet.FunctionalTests/Query/QueryLoggingJetTest.cs

Lines changed: 97 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,33 @@ public QueryLoggingJetTest(NorthwindQueryJetFixture<NoopModelCustomizer> fixture
3131
[ConditionalFact]
3232
public virtual void Queryable_simple()
3333
{
34-
using (var context = CreateContext())
35-
{
36-
var customers
37-
= context.Set<Customer>()
38-
.ToList();
34+
using var context = CreateContext();
35+
var customers
36+
= context.Set<Customer>()
37+
.ToList();
3938

40-
Assert.NotNull(customers);
41-
Assert.StartsWith(
42-
"queryContext => new QueryingEnumerable<Customer>(",
43-
Fixture.TestSqlLoggerFactory.Log[0].Message);
44-
}
39+
Assert.NotNull(customers);
40+
41+
Assert.StartsWith(
42+
"Compiling query expression: ",
43+
Fixture.TestSqlLoggerFactory.Log[0].Message);
44+
Assert.StartsWith(
45+
"Generated query execution expression: " + Environment.NewLine + "'queryContext => new SingleQueryingEnumerable<Customer>(",
46+
Fixture.TestSqlLoggerFactory.Log[1].Message);
47+
}
48+
49+
[ConditionalFact]
50+
public virtual void Queryable_simple_split()
51+
{
52+
using var context = CreateContext();
53+
var customers
54+
= context.Set<Customer>().AsSplitQuery()
55+
.ToList();
56+
57+
Assert.NotNull(customers);
58+
Assert.StartsWith(
59+
"Generated query execution expression: " + Environment.NewLine + "'queryContext => new SplitQueryingEnumerable<Customer>(",
60+
Fixture.TestSqlLoggerFactory.Log[1].Message);
4561
}
4662

4763
[ConditionalFact]
@@ -66,78 +82,88 @@ var customers
6682
}
6783
}
6884

69-
[ConditionalFact(Skip = "Issue#17498")]
70-
public virtual void Query_with_ignored_include_should_log_warning()
85+
[ConditionalFact]
86+
public virtual void Include_navigation()
7187
{
72-
using (var context = CreateContext())
73-
{
74-
var customers
75-
= context.Customers
76-
.Include(c => c.Orders)
77-
.Select(c => c.CustomerID)
78-
.ToList();
88+
using var context = CreateContext();
89+
var customers
90+
= context.Set<Customer>()
91+
.Where(c => c.CustomerID == "ALFKI")
92+
.Include(c => c.Orders)
93+
.ToList();
7994

80-
Assert.NotNull(customers);
81-
Assert.Contains(
82-
#pragma warning disable CS0612 // Type or member is obsolete
83-
CoreResources.LogNavigationBaseIncludeIgnored(new TestLogger<JetLoggingDefinitions>()).GenerateMessage("`c`.Orders"),
84-
Fixture.TestSqlLoggerFactory.Log.Select(l => l.Message));
85-
#pragma warning restore CS0612 // Type or member is obsolete
86-
}
95+
Assert.NotNull(customers);
96+
97+
Assert.Equal(
98+
"Including navigation: 'Customer.Orders'.",
99+
Fixture.TestSqlLoggerFactory.Log[1].Message);
87100
}
88101

89-
[ConditionalFact(Skip = "Issue#17498")]
90-
public virtual void Include_navigation()
102+
[ConditionalFact]
103+
public virtual void Skip_without_order_by()
91104
{
92-
using (var context = CreateContext())
93-
{
94-
var customers
95-
= context.Set<Customer>()
96-
.Include(c => c.Orders)
97-
.ToList();
105+
using var context = CreateContext();
106+
var customers = context.Set<Customer>().Skip(85).ToList();
98107

99-
Assert.NotNull(customers);
108+
Assert.NotNull(customers);
100109

101-
Assert.Equal(
102-
"Compiling query model: " + _eol + "'(from Customer c in DbSet<Customer>" + _eol + @"select `c`).Include(""Orders"")'"
103-
,
104-
Fixture.TestSqlLoggerFactory.Log[0].Message);
105-
Assert.Equal(
106-
"Including navigation: '`c`.Orders'"
107-
,
108-
Fixture.TestSqlLoggerFactory.Log[1].Message);
109-
Assert.StartsWith(
110-
"Optimized query model: "
111-
+ _eol
112-
+ "'from Customer c in DbSet<Customer>"
113-
+ _eol
114-
+ @"order by EF.Property(?`c`?, ""CustomerID"") asc"
115-
+ _eol
116-
+ "select Customer _Include("
117-
,
118-
Fixture.TestSqlLoggerFactory.Log[2].Message);
119-
}
110+
Assert.Equal(
111+
CoreResources.LogRowLimitingOperationWithoutOrderBy(new TestLogger<JetLoggingDefinitions>()).GenerateMessage(),
112+
Fixture.TestSqlLoggerFactory.Log[1].Message);
120113
}
121114

122-
[ConditionalFact(Skip = "Issue #16752")]
123-
public virtual void GroupBy_Include_collection_ignored()
115+
[ConditionalFact]
116+
public virtual void Take_without_order_by()
124117
{
125-
using (var context = CreateContext())
126-
{
127-
var orders = context.Orders
128-
.GroupBy(o => o.OrderID)
129-
.Select(g => g.OrderBy(o => o.OrderID).FirstOrDefault())
130-
.Include(o => o.OrderDetails)
131-
.ToList();
118+
using var context = CreateContext();
119+
var customers = context.Set<Customer>().Take(5).ToList();
132120

133-
Assert.NotNull(orders);
134-
Assert.Contains(
135-
#pragma warning disable CS0612 // Type or member is obsolete
136-
CoreResources.LogNavigationBaseIncludeIgnored(new TestLogger<JetLoggingDefinitions>()).GenerateMessage(
137-
#pragma warning restore CS0612 // Type or member is obsolete
138-
"{from Order o in `g` orderby `o`.OrderID asc select `o` => FirstOrDefault()}.OrderDetails"),
139-
Fixture.TestSqlLoggerFactory.Log.Select(l => l.Message));
140-
}
121+
Assert.NotNull(customers);
122+
123+
Assert.Equal(
124+
CoreResources.LogRowLimitingOperationWithoutOrderBy(new TestLogger<JetLoggingDefinitions>()).GenerateMessage(),
125+
Fixture.TestSqlLoggerFactory.Log[1].Message);
126+
}
127+
128+
[ConditionalFact]
129+
public virtual void FirstOrDefault_without_filter_order_by()
130+
{
131+
using var context = CreateContext();
132+
var customer = context.Set<Customer>().FirstOrDefault();
133+
134+
Assert.NotNull(customer);
135+
136+
Assert.Equal(
137+
CoreResources.LogFirstWithoutOrderByAndFilter(new TestLogger<JetLoggingDefinitions>()).GenerateMessage(),
138+
Fixture.TestSqlLoggerFactory.Log[1].Message);
139+
}
140+
141+
[ConditionalFact]
142+
public virtual void Distinct_used_after_order_by()
143+
{
144+
using var context = CreateContext();
145+
var customers = context.Set<Customer>().OrderBy(x => x.Address).Distinct().Take(5).ToList();
146+
147+
Assert.NotEmpty(customers);
148+
149+
Assert.Equal(
150+
CoreResources.LogDistinctAfterOrderByWithoutRowLimitingOperatorWarning(new TestLogger<JetLoggingDefinitions>())
151+
.GenerateMessage(),
152+
Fixture.TestSqlLoggerFactory.Log[1].Message);
153+
}
154+
155+
[ConditionalFact]
156+
public virtual void Include_collection_does_not_generate_warning()
157+
{
158+
using var context = CreateContext();
159+
var customer = context.Set<Customer>().Include(e => e.Orders).AsSplitQuery().Single(e => e.CustomerID == "ALFKI");
160+
161+
Assert.NotNull(customer);
162+
Assert.Equal(6, customer.Orders.Count);
163+
164+
Assert.DoesNotContain(
165+
CoreResources.LogRowLimitingOperationWithoutOrderBy(new TestLogger<JetLoggingDefinitions>()).GenerateMessage(),
166+
Fixture.TestSqlLoggerFactory.Log.Select(e => e.Message));
141167
}
142168

143169
[ConditionalFact]
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
2-
"Test": {
3-
"Jet": {
4-
"DefaultConnection": "DBQ=Jet.accdb"
5-
}
2+
"Test": {
3+
"Jet": {
4+
"DefaultConnection": "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Jet.accdb;Persist Security Info=False;"
65
}
6+
}
77
}

0 commit comments

Comments
 (0)