diff --git a/src/EFCore.Jet/Query/Internal/JetQueryTranslationPostprocessor.cs b/src/EFCore.Jet/Query/Internal/JetQueryTranslationPostprocessor.cs index d8f041c..8976c19 100644 --- a/src/EFCore.Jet/Query/Internal/JetQueryTranslationPostprocessor.cs +++ b/src/EFCore.Jet/Query/Internal/JetQueryTranslationPostprocessor.cs @@ -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; diff --git a/src/EFCore.Jet/Query/Internal/JetSkipTakePostprocessor.cs b/src/EFCore.Jet/Query/Internal/JetSkipTakePostprocessor.cs index d207705..de78c4e 100644 --- a/src/EFCore.Jet/Query/Internal/JetSkipTakePostprocessor.cs +++ b/src/EFCore.Jet/Query/Internal/JetSkipTakePostprocessor.cs @@ -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 parent = new Stack(); + private readonly QuerySplittingBehavior? _splittingBehavior; /// /// 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 /// public JetSkipTakePostprocessor( IRelationalTypeMappingSource typeMappingSource, - ISqlExpressionFactory sqlExpressionFactory) + ISqlExpressionFactory sqlExpressionFactory, + QuerySplittingBehavior? splittingBehavior) { (_typeMappingSource, _sqlExpressionFactory) = (typeMappingSource, sqlExpressionFactory); + _splittingBehavior = splittingBehavior; } /// @@ -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); } } diff --git a/src/EFCore.Jet/Query/Sql/Internal/JetQuerySqlGenerator.cs b/src/EFCore.Jet/Query/Sql/Internal/JetQuerySqlGenerator.cs index 6de8d66..c2a93b6 100644 --- a/src/EFCore.Jet/Query/Sql/Internal/JetQuerySqlGenerator.cs +++ b/src/EFCore.Jet/Query/Sql/Internal/JetQuerySqlGenerator.cs @@ -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()) { diff --git a/test/EFCore.Jet.FunctionalTests/GreenTests/ace_2010_odbc_x86.txt b/test/EFCore.Jet.FunctionalTests/GreenTests/ace_2010_odbc_x86.txt index 5d95176..4f9c986 100644 --- a/test/EFCore.Jet.FunctionalTests/GreenTests/ace_2010_odbc_x86.txt +++ b/test/EFCore.Jet.FunctionalTests/GreenTests/ace_2010_odbc_x86.txt @@ -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) diff --git a/test/EFCore.Jet.FunctionalTests/GreenTests/ace_2010_oledb_x86.txt b/test/EFCore.Jet.FunctionalTests/GreenTests/ace_2010_oledb_x86.txt index 0dd68c2..96743b5 100644 --- a/test/EFCore.Jet.FunctionalTests/GreenTests/ace_2010_oledb_x86.txt +++ b/test/EFCore.Jet.FunctionalTests/GreenTests/ace_2010_oledb_x86.txt @@ -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) diff --git a/test/EFCore.Jet.FunctionalTests/Query/GearsOfWarQueryJetTest.cs b/test/EFCore.Jet.FunctionalTests/Query/GearsOfWarQueryJetTest.cs index c947a53..aa8432a 100644 --- a/test/EFCore.Jet.FunctionalTests/Query/GearsOfWarQueryJetTest.cs +++ b/test/EFCore.Jet.FunctionalTests/Query/GearsOfWarQueryJetTest.cs @@ -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` diff --git a/test/EFCore.Jet.FunctionalTests/Query/NorthwindMiscellaneousQueryJetTest.cs b/test/EFCore.Jet.FunctionalTests/Query/NorthwindMiscellaneousQueryJetTest.cs index 661cbe7..999dc67 100644 --- a/test/EFCore.Jet.FunctionalTests/Query/NorthwindMiscellaneousQueryJetTest.cs +++ b/test/EFCore.Jet.FunctionalTests/Query/NorthwindMiscellaneousQueryJetTest.cs @@ -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`