DISTINCT AND TOP can't be in the same stateement with Jet. Push DISTINCT into a subquery

7.0-servicing
Christopher Jolly 2 years ago
parent f1878a8950
commit 92ea73548d

@ -147,6 +147,13 @@ public class JetQueryableMethodTranslatingExpressionVisitor : RelationalQueryabl
}*/ }*/
} }
//With Jet DISTINCT and TOP can't be used together in the same statement
//Make the DISTINCT into a subquery
if (selectExpression.IsDistinct)
{
selectExpression.PushdownIntoSubquery();
}
return base.TranslateTake(source, count); return base.TranslateTake(source, count);
} }

@ -2092,13 +2092,16 @@ ORDER BY `t`.`OrderID`");
await base.Distinct_Take_Count(isAsync); await base.Distinct_Take_Count(isAsync);
AssertSql( AssertSql(
$@"{AssertSqlHelper.Declaration("@__p_0='5'")} """
SELECT COUNT(*) SELECT COUNT(*)
FROM ( FROM (
SELECT DISTINCT TOP {AssertSqlHelper.Parameter("@__p_0")} `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate` SELECT TOP 5 `t`.`OrderID`
FROM `Orders` AS `o` FROM (
) AS `t`"); SELECT DISTINCT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
) AS `t`
) AS `t0`
""");
} }
public override async Task OrderBy_shadow(bool isAsync) public override async Task OrderBy_shadow(bool isAsync)
@ -3293,17 +3296,21 @@ FROM (
await base.OrderBy_coalesce_skip_take_distinct_take(isAsync); await base.OrderBy_coalesce_skip_take_distinct_take(isAsync);
AssertSql( AssertSql(
$@"{AssertSqlHelper.Declaration("@__p_0='5'")} """
SELECT TOP 5 `t1`.`ProductID`, `t1`.`Discontinued`, `t1`.`ProductName`, `t1`.`SupplierID`, `t1`.`UnitPrice`, `t1`.`UnitsInStock`
{AssertSqlHelper.Declaration("@__p_1='15'")}
SELECT DISTINCT TOP {AssertSqlHelper.Parameter("@__p_0")} `t`.`ProductID`, `t`.`Discontinued`, `t`.`ProductName`, `t`.`SupplierID`, `t`.`UnitPrice`, `t`.`UnitsInStock`
FROM ( FROM (
SELECT `p`.`ProductID`, `p`.`Discontinued`, `p`.`ProductName`, `p`.`SupplierID`, `p`.`UnitPrice`, `p`.`UnitsInStock`, IIF(`p`.`UnitPrice` IS NULL, NULL, `p`.`UnitPrice`) AS `c` SELECT DISTINCT `t0`.`ProductID`, `t0`.`Discontinued`, `t0`.`ProductName`, `t0`.`SupplierID`, `t0`.`UnitPrice`, `t0`.`UnitsInStock`
FROM `Products` AS `p` FROM (
ORDER BY IIF(`p`.`UnitPrice` IS NULL, NULL, `p`.`UnitPrice`) SELECT TOP 15 `t`.`ProductID`, `t`.`Discontinued`, `t`.`ProductName`, `t`.`SupplierID`, `t`.`UnitPrice`, `t`.`UnitsInStock`
SKIP {AssertSqlHelper.Parameter("@__p_0")} FETCH NEXT {AssertSqlHelper.Parameter("@__p_1")} ROWS ONLY FROM (
) AS `t`"); 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`
FROM `Products` AS `p`
ORDER BY IIF(`p`.`UnitPrice` IS NULL, 0.0, `p`.`UnitPrice`)
) AS `t`
ORDER BY `t`.`c` DESC
) AS `t0`
) AS `t1`
""");
} }
public override async Task OrderBy_skip_take_distinct_orderby_take(bool isAsync) public override async Task OrderBy_skip_take_distinct_orderby_take(bool isAsync)

@ -30,113 +30,137 @@ namespace EntityFrameworkCore.Jet.FunctionalTests.Query
[ConditionalFact] [ConditionalFact]
public virtual void Queryable_simple() public virtual void Queryable_simple()
{ {
using (var context = CreateContext()) using var context = CreateContext();
{ var customers
var customers = context.Set<Customer>()
= context.Set<Customer>() .ToList();
.ToList();
Assert.NotNull(customers);
Assert.NotNull(customers);
Assert.StartsWith( Assert.StartsWith(
"queryContext => new QueryingEnumerable<Customer>(", "Compiling query expression: ",
Fixture.TestSqlLoggerFactory.Log[0].Message); Fixture.TestSqlLoggerFactory.Log[0].Message);
} Assert.StartsWith(
"Generated query execution expression: " + Environment.NewLine + "'queryContext => new SingleQueryingEnumerable<Customer>(",
Fixture.TestSqlLoggerFactory.Log[1].Message);
} }
[ConditionalFact] [ConditionalFact]
public virtual void Queryable_with_parameter_outputs_parameter_value_logging_warning() public virtual void Queryable_simple_split()
{ {
using (var context = CreateContext()) using var context = CreateContext();
{ var customers
context.GetInfrastructure().GetRequiredService<IDiagnosticsLogger<DbLoggerCategory.Query>>() = context.Set<Customer>().AsSplitQuery()
.Options.IsSensitiveDataLoggingWarned = false; .ToList();
// ReSharper disable once ConvertToConstant.Local
var city = "Redmond"; Assert.NotNull(customers);
Assert.StartsWith(
var customers "Generated query execution expression: " + Environment.NewLine + "'queryContext => new SplitQueryingEnumerable<Customer>(",
= context.Customers Fixture.TestSqlLoggerFactory.Log[1].Message);
.Where(c => c.City == city)
.ToList();
Assert.NotNull(customers);
Assert.Contains(
CoreResources.LogSensitiveDataLoggingEnabled(new TestLogger<JetLoggingDefinitions>()).GenerateMessage(),
Fixture.TestSqlLoggerFactory.Log.Select(l => l.Message));
}
} }
[ConditionalFact(Skip = "Issue#17498")] [ConditionalFact]
public virtual void Query_with_ignored_include_should_log_warning() public virtual void Queryable_with_parameter_outputs_parameter_value_logging_warning()
{ {
using (var context = CreateContext()) using var context = CreateContext();
{ context.GetInfrastructure().GetRequiredService<IDiagnosticsLogger<DbLoggerCategory.Query>>()
var customers .Options.IsSensitiveDataLoggingWarned = false;
= context.Customers // ReSharper disable once ConvertToConstant.Local
.Include(c => c.Orders) var city = "Redmond";
.Select(c => c.CustomerID)
.ToList(); var customers
= context.Customers
Assert.NotNull(customers); .Where(c => c.City == city)
Assert.Contains( .ToList();
#pragma warning disable CS0612 // Type or member is obsolete
CoreResources.LogNavigationBaseIncludeIgnored(new TestLogger<JetLoggingDefinitions>()).GenerateMessage("`c`.Orders"), Assert.NotNull(customers);
Fixture.TestSqlLoggerFactory.Log.Select(l => l.Message)); Assert.Contains(
#pragma warning restore CS0612 // Type or member is obsolete CoreResources.LogSensitiveDataLoggingEnabled(new TestLogger<JetLoggingDefinitions>()).GenerateMessage(),
} Fixture.TestSqlLoggerFactory.Log.Select(l => l.Message));
} }
[ConditionalFact(Skip = "Issue#17498")] [ConditionalFact]
public virtual void Include_navigation() public virtual void Include_navigation()
{ {
using (var context = CreateContext()) using var context = CreateContext();
{ var customers
var customers = context.Set<Customer>()
= context.Set<Customer>() .Where(c => c.CustomerID == "ALFKI")
.Include(c => c.Orders) .Include(c => c.Orders)
.ToList(); .ToList();
Assert.NotNull(customers); Assert.NotNull(customers);
Assert.Equal( Assert.Equal(
"Compiling query model: " + _eol + "'(from Customer c in DbSet<Customer>" + _eol + @"select `c`).Include(""Orders"")'" "Including navigation: 'Customer.Orders'.",
, Fixture.TestSqlLoggerFactory.Log[1].Message);
Fixture.TestSqlLoggerFactory.Log[0].Message);
Assert.Equal(
"Including navigation: '`c`.Orders'"
,
Fixture.TestSqlLoggerFactory.Log[1].Message);
Assert.StartsWith(
"Optimized query model: "
+ _eol
+ "'from Customer c in DbSet<Customer>"
+ _eol
+ @"order by EF.Property(?`c`?, ""CustomerID"") asc"
+ _eol
+ "select Customer _Include("
,
Fixture.TestSqlLoggerFactory.Log[2].Message);
}
} }
[ConditionalFact(Skip = "Issue #16752")] [ConditionalFact]
public virtual void GroupBy_Include_collection_ignored() public virtual void Skip_without_order_by()
{ {
using (var context = CreateContext()) using var context = CreateContext();
{ var customers = context.Set<Customer>().Skip(85).ToList();
var orders = context.Orders
.GroupBy(o => o.OrderID)
.Select(g => g.OrderBy(o => o.OrderID).FirstOrDefault())
.Include(o => o.OrderDetails)
.ToList();
Assert.NotNull(orders); Assert.NotNull(customers);
Assert.Contains(
#pragma warning disable CS0612 // Type or member is obsolete Assert.Equal(
CoreResources.LogNavigationBaseIncludeIgnored(new TestLogger<JetLoggingDefinitions>()).GenerateMessage( CoreResources.LogRowLimitingOperationWithoutOrderBy(new TestLogger<JetLoggingDefinitions>()).GenerateMessage(),
#pragma warning restore CS0612 // Type or member is obsolete Fixture.TestSqlLoggerFactory.Log[1].Message);
"{from Order o in `g` orderby `o`.OrderID asc select `o` => FirstOrDefault()}.OrderDetails"), }
Fixture.TestSqlLoggerFactory.Log.Select(l => l.Message));
} [ConditionalFact]
public virtual void Take_without_order_by()
{
using var context = CreateContext();
var customers = context.Set<Customer>().Take(5).ToList();
Assert.NotNull(customers);
Assert.Equal(
CoreResources.LogRowLimitingOperationWithoutOrderBy(new TestLogger<JetLoggingDefinitions>()).GenerateMessage(),
Fixture.TestSqlLoggerFactory.Log[1].Message);
}
[ConditionalFact]
public virtual void FirstOrDefault_without_filter_order_by()
{
using var context = CreateContext();
var customer = context.Set<Customer>().FirstOrDefault();
Assert.NotNull(customer);
Assert.Equal(
CoreResources.LogFirstWithoutOrderByAndFilter(new TestLogger<JetLoggingDefinitions>()).GenerateMessage(),
Fixture.TestSqlLoggerFactory.Log[1].Message);
}
[ConditionalFact]
public virtual void Distinct_used_after_order_by()
{
using var context = CreateContext();
var customers = context.Set<Customer>().OrderBy(x => x.Address).Distinct().Take(5).ToList();
Assert.NotEmpty(customers);
Assert.Equal(
CoreResources.LogDistinctAfterOrderByWithoutRowLimitingOperatorWarning(new TestLogger<JetLoggingDefinitions>())
.GenerateMessage(),
Fixture.TestSqlLoggerFactory.Log[1].Message);
}
[ConditionalFact]
public virtual void Include_collection_does_not_generate_warning()
{
using var context = CreateContext();
var customer = context.Set<Customer>().Include(e => e.Orders).AsSplitQuery().Single(e => e.CustomerID == "ALFKI");
Assert.NotNull(customer);
Assert.Equal(6, customer.Orders.Count);
Assert.DoesNotContain(
CoreResources.LogRowLimitingOperationWithoutOrderBy(new TestLogger<JetLoggingDefinitions>()).GenerateMessage(),
Fixture.TestSqlLoggerFactory.Log.Select(e => e.Message));
} }
[ConditionalFact] [ConditionalFact]

Loading…
Cancel
Save