Optimization to throw on a split query with offset but no order by (#209)

pull/210/head
Christopher Jolly 2 years ago committed by GitHub
parent 9a41af323d
commit e797aa7b94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -31,7 +31,7 @@ namespace EntityFrameworkCore.Jet.Query.Internal
_options = options;
_liftOrderByPostprocessor = new JetLiftOrderByPostprocessor(relationalTypeMappingSource, relationalDependencies.SqlExpressionFactory);
_skipTakePostprocessor = new JetSkipTakePostprocessor(relationalTypeMappingSource,
relationalDependencies.SqlExpressionFactory);
relationalDependencies.SqlExpressionFactory, ((RelationalQueryCompilationContext)QueryCompilationContext).QuerySplittingBehavior);
}
public override Expression Process(Expression query)
@ -39,12 +39,13 @@ namespace EntityFrameworkCore.Jet.Query.Internal
query = _skipTakePostprocessor.Process(query);
query = base.Process(query);
//query = _skipTakePostprocessor.Process(query);
if (_options.EnableMillisecondsSupport)
{
query = new JetDateTimeExpressionVisitor(RelationalDependencies.SqlExpressionFactory, _relationalTypeMappingSource).Visit(query);
}
query = _skipWithoutOrderByInSplitQueryVerifier.Visit(query);
//query = _skipWithoutOrderByInSplitQueryVerifier.Visit(query);
//query = _skipTakePostprocessor.Process(query);
query = _liftOrderByPostprocessor.Process(query);
return query;

@ -4,6 +4,8 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using EntityFrameworkCore.Jet.Internal;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
@ -22,6 +24,7 @@ public class JetSkipTakePostprocessor : ExpressionVisitor
private readonly IRelationalTypeMappingSource _typeMappingSource;
private readonly ISqlExpressionFactory _sqlExpressionFactory;
private Stack<SelectExpression> parent = new Stack<SelectExpression>();
private readonly QuerySplittingBehavior? _splittingBehavior;
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@ -30,9 +33,11 @@ public class JetSkipTakePostprocessor : ExpressionVisitor
/// </summary>
public JetSkipTakePostprocessor(
IRelationalTypeMappingSource typeMappingSource,
ISqlExpressionFactory sqlExpressionFactory)
ISqlExpressionFactory sqlExpressionFactory,
QuerySplittingBehavior? splittingBehavior)
{
(_typeMappingSource, _sqlExpressionFactory) = (typeMappingSource, sqlExpressionFactory);
_splittingBehavior = splittingBehavior;
}
/// <summary>
@ -61,55 +66,45 @@ public class JetSkipTakePostprocessor : ExpressionVisitor
return shapedQueryExpression.UpdateQueryExpression(Visit(shapedQueryExpression.QueryExpression));
case SelectExpression selectExpression:
{
if (selectExpression.Offset is not null && selectExpression.Limit is not null)
if (selectExpression.Orderings.Count == 0 && selectExpression.Offset is not null && _splittingBehavior == QuerySplittingBehavior.SplitQuery)
{
throw new InvalidOperationException(JetStrings.SplitQueryOffsetWithoutOrderBy);
}
else if (selectExpression.Offset is not null && selectExpression.Limit is not null)
{
SqlExpression offset = selectExpression.Offset!;
SqlExpression limit = selectExpression.Limit!;
var total = new SqlBinaryExpression(ExpressionType.Add, offset, limit, typeof(int),
RelationalTypeMapping.NullMapping);
MethodInfo? dynMethodO = selectExpression.GetType().GetMethod("set_Offset",
BindingFlags.NonPublic | BindingFlags.Instance);
MethodInfo? dynMethod1 = selectExpression.GetType().GetMethod("set_Limit",
BindingFlags.NonPublic | BindingFlags.Instance);
SqlExpression mynullexp = null!;
dynMethodO?.Invoke(selectExpression, new object[] { mynullexp });
dynMethod1?.Invoke(selectExpression, new object[] { mynullexp });
var total = new SqlBinaryExpression(ExpressionType.Add, offset, limit, typeof(int),
RelationalTypeMapping.NullMapping);
selectExpression.ApplyLimit(total);
dynMethod1?.Invoke(selectExpression, new object[] { total });
selectExpression.ReverseOrderings();
selectExpression.ApplyLimit(limit);
parent.TryPeek(out var parentselect);
if (parentselect != null)
if (parentselect != null && parentselect.Orderings.Count > 0)
{
if (parentselect.Orderings.Count > 0)
{
}
else
{
{
selectExpression.ReverseOrderings();
}
}
}
else
{
selectExpression.ReverseOrderings();
}
}
parent.Push(selectExpression);
selectExpression = (SelectExpression)base.Visit(selectExpression);
var newselectExpression = (SelectExpression)base.Visit(selectExpression);
parent.Pop();
return selectExpression;
return newselectExpression;
}
default:
return base.Visit(expression);
}
return base.Visit(expression);
}
}

@ -91,15 +91,7 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
Sql.Append("DISTINCT ");
}
if (selectExpression.Tags.Contains("DeepSkip"))
{
}
else
{
GenerateTop(selectExpression);
}
GenerateTop(selectExpression);
if (selectExpression.Projection.Any())
{

@ -11511,6 +11511,8 @@ EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeNoTrackingQue
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeNoTrackingQueryJetTest.Include_collection_single_or_default_no_result(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeNoTrackingQueryJetTest.Include_collection_skip_no_order_by(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeNoTrackingQueryJetTest.Include_collection_skip_no_order_by(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeNoTrackingQueryJetTest.Include_collection_skip_take_no_order_by(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeNoTrackingQueryJetTest.Include_collection_skip_take_no_order_by(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeNoTrackingQueryJetTest.Include_collection_take_no_order_by(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeNoTrackingQueryJetTest.Include_collection_take_no_order_by(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeNoTrackingQueryJetTest.Include_collection_then_include_collection_predicate(async: False)
@ -11688,6 +11690,8 @@ EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeQueryJetTest.
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeQueryJetTest.Include_collection_single_or_default_no_result(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeQueryJetTest.Include_collection_skip_no_order_by(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeQueryJetTest.Include_collection_skip_no_order_by(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeQueryJetTest.Include_collection_skip_take_no_order_by(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeQueryJetTest.Include_collection_skip_take_no_order_by(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeQueryJetTest.Include_collection_take_no_order_by(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeQueryJetTest.Include_collection_take_no_order_by(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeQueryJetTest.Include_collection_then_include_collection_predicate(async: False)

@ -14776,6 +14776,8 @@ EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeNoTrackingQue
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeNoTrackingQueryJetTest.Include_collection_single_or_default_no_result(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeNoTrackingQueryJetTest.Include_collection_skip_no_order_by(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeNoTrackingQueryJetTest.Include_collection_skip_no_order_by(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeNoTrackingQueryJetTest.Include_collection_skip_take_no_order_by(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeNoTrackingQueryJetTest.Include_collection_skip_take_no_order_by(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeNoTrackingQueryJetTest.Include_collection_take_no_order_by(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeNoTrackingQueryJetTest.Include_collection_take_no_order_by(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeNoTrackingQueryJetTest.Include_collection_then_include_collection_predicate(async: False)
@ -14953,6 +14955,8 @@ EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeQueryJetTest.
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeQueryJetTest.Include_collection_single_or_default_no_result(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeQueryJetTest.Include_collection_skip_no_order_by(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeQueryJetTest.Include_collection_skip_no_order_by(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeQueryJetTest.Include_collection_skip_take_no_order_by(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeQueryJetTest.Include_collection_skip_take_no_order_by(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeQueryJetTest.Include_collection_take_no_order_by(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeQueryJetTest.Include_collection_take_no_order_by(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindSplitIncludeQueryJetTest.Include_collection_then_include_collection_predicate(async: False)

@ -8851,7 +8851,7 @@ LEFT JOIN `LocustHighCommands` AS `l0` ON `l`.`HighCommandId` = `l0`.`Id`
await base.Join_entity_with_itself_grouped_by_key_followed_by_include_skip_take(async);
AssertSql(
"""
"""
SELECT `t1`.`Nickname`, `t1`.`SquadId`, `t1`.`AssignedCityName`, `t1`.`CityOfBirthName`, `t1`.`Discriminator`, `t1`.`FullName`, `t1`.`HasSoulPatch`, `t1`.`LeaderNickname`, `t1`.`LeaderSquadId`, `t1`.`Rank`, `t1`.`HasSoulPatch0`, `w`.`Id`, `w`.`AmmunitionType`, `w`.`IsAutomatic`, `w`.`Name`, `w`.`OwnerFullName`, `w`.`SynergyWithId`
FROM (
SELECT TOP 10 `t0`.`Nickname`, `t0`.`SquadId`, `t0`.`AssignedCityName`, `t0`.`CityOfBirthName`, `t0`.`Discriminator`, `t0`.`FullName`, `t0`.`HasSoulPatch`, `t0`.`LeaderNickname`, `t0`.`LeaderSquadId`, `t0`.`Rank`, `t0`.`HasSoulPatch0`

@ -3995,21 +3995,21 @@ ORDER BY `t`.`c`
await base.Include_with_orderby_skip_preserves_ordering(isAsync);
AssertSql(
"""
SELECT `t0`.`CustomerID`, `t0`.`Address`, `t0`.`City`, `t0`.`CompanyName`, `t0`.`ContactName`, `t0`.`ContactTitle`, `t0`.`Country`, `t0`.`Fax`, `t0`.`Phone`, `t0`.`PostalCode`, `t0`.`Region`, `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
"""
SELECT `t0`.`CustomerID`, `t0`.`Address`, `t0`.`City`, `t0`.`CompanyName`, `t0`.`ContactName`, `t0`.`ContactTitle`, `t0`.`Country`, `t0`.`Fax`, `t0`.`Phone`, `t0`.`PostalCode`, `t0`.`Region`, `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM (
SELECT TOP 5 `t`.`CustomerID`, `t`.`Address`, `t`.`City`, `t`.`CompanyName`, `t`.`ContactName`, `t`.`ContactTitle`, `t`.`Country`, `t`.`Fax`, `t`.`Phone`, `t`.`PostalCode`, `t`.`Region`
FROM (
SELECT TOP 5 `t`.`CustomerID`, `t`.`Address`, `t`.`City`, `t`.`CompanyName`, `t`.`ContactName`, `t`.`ContactTitle`, `t`.`Country`, `t`.`Fax`, `t`.`Phone`, `t`.`PostalCode`, `t`.`Region`
FROM (
SELECT TOP 45 `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
FROM `Customers` AS `c`
WHERE `c`.`CustomerID` NOT IN ('VAFFE', 'DRACD')
ORDER BY `c`.`City`, `c`.`CustomerID`
) AS `t`
ORDER BY `t`.`City` DESC, `t`.`CustomerID` DESC
) AS `t0`
LEFT JOIN `Orders` AS `o` ON `t0`.`CustomerID` = `o`.`CustomerID`
ORDER BY `t0`.`City`, `t0`.`CustomerID`
""");
SELECT TOP 45 `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
FROM `Customers` AS `c`
WHERE `c`.`CustomerID` NOT IN ('VAFFE', 'DRACD')
ORDER BY `c`.`City`, `c`.`CustomerID`
) AS `t`
ORDER BY `t`.`City` DESC, `t`.`CustomerID` DESC
) AS `t0`
LEFT JOIN `Orders` AS `o` ON `t0`.`CustomerID` = `o`.`CustomerID`
ORDER BY `t0`.`City`, `t0`.`CustomerID`
""");
}
public override async Task Int16_parameter_can_be_used_for_int_column(bool isAsync)
@ -5028,7 +5028,7 @@ ORDER BY `t`.`OrderID`, `o0`.`OrderID`
await base.Collection_projection_skip_take(async);
AssertSql(
"""
"""
SELECT `t0`.`OrderID`, `t0`.`CustomerID`, `t0`.`EmployeeID`, `t0`.`OrderDate`, `o0`.`OrderID`, `o0`.`ProductID`, `o0`.`Discount`, `o0`.`Quantity`, `o0`.`UnitPrice`
FROM (
SELECT TOP 10 `t`.`OrderID`, `t`.`CustomerID`, `t`.`EmployeeID`, `t`.`OrderDate`

Loading…
Cancel
Save