Reintroduce the SkipTakeCollapsingExpressionVisitor (#200)

pull/202/head
Christopher Jolly 2 years ago committed by GitHub
parent 99c266297e
commit 12bbf0470f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -554,12 +554,11 @@ namespace EntityFrameworkCore.Jet.Data
// in TOP clauses. // in TOP clauses.
var parameters = InnerCommand.Parameters.Cast<DbParameter>() var parameters = InnerCommand.Parameters.Cast<DbParameter>()
.ToList(); .ToList();
var lastCommandText = InnerCommand.CommandText;
var commandText = lastCommandText;
if (parameters.Count > 0) if (parameters.Count > 0)
{ {
var lastCommandText = InnerCommand.CommandText;
var commandText = lastCommandText;
while ((commandText = _topMultiParameterRegularExpression.Replace( while ((commandText = _topMultiParameterRegularExpression.Replace(
lastCommandText, lastCommandText,
match => match =>
@ -587,24 +586,23 @@ namespace EntityFrameworkCore.Jet.Data
lastCommandText = commandText; lastCommandText = commandText;
} }
while ((commandText = _topMultiArgumentRegularExpression.Replace(
lastCommandText,
match =>
{
var first = match.Groups["first"];
var sec = match.Groups["sec"];
return (Convert.ToInt32(first.Value) + Convert.ToInt32(sec.Value)).ToString();
},
1)) != lastCommandText)
{
lastCommandText = commandText;
}
InnerCommand.CommandText = commandText;
InnerCommand.Parameters.Clear(); InnerCommand.Parameters.Clear();
InnerCommand.Parameters.AddRange(parameters.ToArray()); InnerCommand.Parameters.AddRange(parameters.ToArray());
} }
while ((commandText = _topMultiArgumentRegularExpression.Replace(
lastCommandText,
match =>
{
var first = match.Groups["first"];
var sec = match.Groups["sec"];
return (Convert.ToInt32(first.Value) + Convert.ToInt32(sec.Value)).ToString();
},
1)) != lastCommandText)
{
lastCommandText = commandText;
}
InnerCommand.CommandText = commandText;
} }
private void ModifyOuterSelectTopValueForOuterSelectSkipEmulationViaDataReader() private void ModifyOuterSelectTopValueForOuterSelectSkipEmulationViaDataReader()

@ -40,19 +40,19 @@ public class JetParameterBasedSqlProcessor : RelationalParameterBasedSqlProcesso
IReadOnlyDictionary<string, object?> parametersValues, IReadOnlyDictionary<string, object?> parametersValues,
out bool canCache) out bool canCache)
{ {
queryExpression = base.Optimize(queryExpression, parametersValues, out canCache); var optimizedQueryExpression = base.Optimize(queryExpression, parametersValues, out canCache);
/*optimizedQueryExpression = new SkipTakeCollapsingExpressionVisitor(Dependencies.SqlExpressionFactory) optimizedQueryExpression = new SkipTakeCollapsingExpressionVisitor(Dependencies.SqlExpressionFactory)
.Process(optimizedQueryExpression, parametersValues, out var canCache2);*/ .Process(optimizedQueryExpression, parametersValues, out var canCache2);
//canCache &= canCache2; canCache &= canCache2;
queryExpression = new SearchConditionConvertingExpressionVisitor(Dependencies.SqlExpressionFactory).Visit(queryExpression); optimizedQueryExpression = new SearchConditionConvertingExpressionVisitor(Dependencies.SqlExpressionFactory).Visit(optimizedQueryExpression);
// Run the compatibility checks as late in the query pipeline (before the actual SQL translation happens) as reasonable. // Run the compatibility checks as late in the query pipeline (before the actual SQL translation happens) as reasonable.
queryExpression = new JetCompatibilityExpressionVisitor().Visit(queryExpression); optimizedQueryExpression = new JetCompatibilityExpressionVisitor().Visit(optimizedQueryExpression);
return queryExpression; return optimizedQueryExpression;
} }
/// <inheritdoc /> /// <inheritdoc />

@ -0,0 +1,106 @@
using System.Collections.Generic;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
namespace EntityFrameworkCore.Jet.Query.Internal;
/// <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
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public class SkipTakeCollapsingExpressionVisitor : ExpressionVisitor
{
private readonly ISqlExpressionFactory _sqlExpressionFactory;
private IReadOnlyDictionary<string, object?> _parameterValues;
private bool _canCache;
/// <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
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public SkipTakeCollapsingExpressionVisitor(ISqlExpressionFactory sqlExpressionFactory)
{
_sqlExpressionFactory = sqlExpressionFactory;
_parameterValues = null!;
}
/// <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
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual Expression Process(
Expression queryExpression,
IReadOnlyDictionary<string, object?> parametersValues,
out bool canCache)
{
_parameterValues = parametersValues;
_canCache = true;
var result = Visit(queryExpression);
canCache = _canCache;
return result;
}
/// <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
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
protected override Expression VisitExtension(Expression extensionExpression)
{
if (extensionExpression is SelectExpression selectExpression)
{
if (IsZero(selectExpression.Limit))
{
var result = selectExpression.Update(
selectExpression.Projection,
selectExpression.Tables,
selectExpression.GroupBy.Count > 0 ? selectExpression.Predicate : _sqlExpressionFactory.Constant(false),
selectExpression.GroupBy,
selectExpression.GroupBy.Count > 0 ? _sqlExpressionFactory.Constant(false) : null,
new List<OrderingExpression>(0),
limit: null,
offset: null);
return base.VisitExtension(result);
}
bool IsZero(SqlExpression? sqlExpression)
{
switch (sqlExpression)
{
case SqlConstantExpression { Value: int intValue }:
return intValue == 0;
case SqlParameterExpression parameter:
_canCache = false;
return _parameterValues[parameter.Name] is 0;
case SqlBinaryExpression { Left: SqlConstantExpression left, Right: SqlConstantExpression right }:
return left.Value is int leftValue && right.Value is int rightValue && leftValue + rightValue == 0;
case SqlBinaryExpression { Left: SqlParameterExpression left, Right: SqlConstantExpression right }:
_canCache = false;
return _parameterValues[left.Name] is 0 && right.Value is int and 0;
case SqlBinaryExpression { Left: SqlConstantExpression left, Right: SqlParameterExpression right }:
_canCache = false;
return _parameterValues[right.Name] is 0 && left.Value is int and 0;
case SqlBinaryExpression { Left: SqlParameterExpression left, Right: SqlParameterExpression right }:
_canCache = false;
return _parameterValues[left.Name] is 0 && _parameterValues[right.Name] is 0;
default:
return false;
}
}
}
return base.VisitExtension(extensionExpression);
}
}

@ -9156,6 +9156,8 @@ EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.Group
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_after_anonymous_projection_and_distinct_followed_by_another_anonymous_projection(async: True) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_after_anonymous_projection_and_distinct_followed_by_another_anonymous_projection(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_after_predicate_Constant_Select_Sum_Min_Key_Max_Avg(isAsync: False) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_after_predicate_Constant_Select_Sum_Min_Key_Max_Avg(isAsync: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_after_predicate_Constant_Select_Sum_Min_Key_Max_Avg(isAsync: True) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_after_predicate_Constant_Select_Sum_Min_Key_Max_Avg(isAsync: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_aggregate_after_skip_0_take_0(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_aggregate_after_skip_0_take_0(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_aggregate_Contains(isAsync: False) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_aggregate_Contains(isAsync: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_aggregate_Contains(isAsync: True) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_aggregate_Contains(isAsync: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_aggregate_followed_another_GroupBy_aggregate(async: False) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_aggregate_followed_another_GroupBy_aggregate(async: False)
@ -10590,6 +10592,10 @@ EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Shaper_command_caching_when_parameter_names_different(isAsync: False) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Shaper_command_caching_when_parameter_names_different(isAsync: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Shaper_command_caching_when_parameter_names_different(isAsync: True) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Shaper_command_caching_when_parameter_names_different(isAsync: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Single_Predicate_Cancellation EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Single_Predicate_Cancellation
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Skip_0_Take_0_works_when_constant(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Skip_0_Take_0_works_when_constant(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Skip_0_Take_0_works_when_parameter(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Skip_0_Take_0_works_when_parameter(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Skip_Take_All(isAsync: False) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Skip_Take_All(isAsync: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Skip_Take_All(isAsync: True) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Skip_Take_All(isAsync: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Skip_Take_Any_with_predicate(isAsync: False) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Skip_Take_Any_with_predicate(isAsync: False)

@ -12068,6 +12068,8 @@ EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.Group
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_after_anonymous_projection_and_distinct_followed_by_another_anonymous_projection(async: True) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_after_anonymous_projection_and_distinct_followed_by_another_anonymous_projection(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_after_predicate_Constant_Select_Sum_Min_Key_Max_Avg(isAsync: False) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_after_predicate_Constant_Select_Sum_Min_Key_Max_Avg(isAsync: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_after_predicate_Constant_Select_Sum_Min_Key_Max_Avg(isAsync: True) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_after_predicate_Constant_Select_Sum_Min_Key_Max_Avg(isAsync: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_aggregate_after_skip_0_take_0(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_aggregate_after_skip_0_take_0(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_aggregate_Contains(isAsync: False) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_aggregate_Contains(isAsync: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_aggregate_Contains(isAsync: True) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_aggregate_Contains(isAsync: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_aggregate_followed_another_GroupBy_aggregate(async: False) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindGroupByQueryJetTest.GroupBy_aggregate_followed_another_GroupBy_aggregate(async: False)
@ -13522,6 +13524,10 @@ EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Shaper_command_caching_when_parameter_names_different(isAsync: False) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Shaper_command_caching_when_parameter_names_different(isAsync: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Shaper_command_caching_when_parameter_names_different(isAsync: True) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Shaper_command_caching_when_parameter_names_different(isAsync: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Single_Predicate_Cancellation EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Single_Predicate_Cancellation
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Skip_0_Take_0_works_when_constant(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Skip_0_Take_0_works_when_constant(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Skip_0_Take_0_works_when_parameter(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Skip_0_Take_0_works_when_parameter(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Skip_Take_All(isAsync: False) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Skip_Take_All(isAsync: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Skip_Take_All(isAsync: True) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Skip_Take_All(isAsync: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Skip_Take_Any_with_predicate(isAsync: False) EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Skip_Take_Any_with_predicate(isAsync: False)

@ -2758,13 +2758,17 @@ INNER JOIN (
AssertSql( AssertSql(
""" """
SELECT [t].[CustomerID] AS [Key], COUNT(*) AS [Total] SELECT `t0`.`CustomerID` AS `Key`, COUNT(*) AS `Total`
FROM ( FROM (
SELECT [o].[CustomerID] SELECT `t`.`CustomerID`
FROM [Orders] AS [o] FROM (
SELECT `o`.`CustomerID`
FROM `Orders` AS `o`
WHERE 0 = 1
) AS `t`
WHERE 0 = 1 WHERE 0 = 1
) AS [t] ) AS `t0`
GROUP BY [t].[CustomerID] GROUP BY `t0`.`CustomerID`
"""); """);
} }

@ -5465,18 +5465,31 @@ CROSS APPLY (
AssertSql( AssertSql(
""" """
SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] SELECT `t0`.`CustomerID`, `t0`.`Address`, `t0`.`City`, `t0`.`CompanyName`, `t0`.`ContactName`, `t0`.`ContactTitle`, `t0`.`Country`, `t0`.`Fax`, `t0`.`Phone`, `t0`.`PostalCode`, `t0`.`Region`
FROM [Customers] AS [c] FROM (
WHERE 0 = 1 SELECT `t`.`CustomerID`, `t`.`Address`, `t`.`City`, `t`.`CompanyName`, `t`.`ContactName`, `t`.`ContactTitle`, `t`.`Country`, `t`.`Fax`, `t`.`Phone`, `t`.`PostalCode`, `t`.`Region`
FROM (
SELECT `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 0 = 1
) AS `t`
WHERE 0 = 1
) AS `t0`
ORDER BY `t0`.`CustomerID`
""", """,
// //
""" """
@__p_0='1' SELECT `t0`.`CustomerID`, `t0`.`Address`, `t0`.`City`, `t0`.`CompanyName`, `t0`.`ContactName`, `t0`.`ContactTitle`, `t0`.`Country`, `t0`.`Fax`, `t0`.`Phone`, `t0`.`PostalCode`, `t0`.`Region`
FROM (
SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] SELECT TOP 1 `t`.`CustomerID`, `t`.`Address`, `t`.`City`, `t`.`CompanyName`, `t`.`ContactName`, `t`.`ContactTitle`, `t`.`Country`, `t`.`Fax`, `t`.`Phone`, `t`.`PostalCode`, `t`.`Region`
FROM [Customers] AS [c] FROM (
ORDER BY [c].[CustomerID] SELECT TOP 2 `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
OFFSET @__p_0 ROWS FETCH NEXT @__p_0 ROWS ONLY FROM `Customers` AS `c`
ORDER BY `c`.`CustomerID`
) AS `t`
ORDER BY `t`.`CustomerID` DESC
) AS `t0`
ORDER BY `t0`.`CustomerID`
"""); """);
} }
@ -5486,16 +5499,20 @@ OFFSET @__p_0 ROWS FETCH NEXT @__p_0 ROWS ONLY
AssertSql( AssertSql(
""" """
SELECT CASE SELECT IIF(EXISTS (
WHEN EXISTS (
SELECT 1 SELECT 1
FROM [Orders] AS [o] FROM (
WHERE 0 = 1) THEN CAST(1 AS bit) SELECT `t`.`OrderID`, `t`.`CustomerID`, `t`.`EmployeeID`, `t`.`OrderDate`
ELSE CAST(0 AS bit) FROM (
END SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM [Customers] AS [c] FROM `Orders` AS `o`
WHERE [c].[CustomerID] LIKE N'F%' WHERE 0 = 1
ORDER BY [c].[CustomerID] ) AS `t`
WHERE 0 = 1
) AS `t0`), TRUE, FALSE)
FROM `Customers` AS `c`
WHERE `c`.`CustomerID` LIKE 'F%'
ORDER BY `c`.`CustomerID`
"""); """);
} }

Loading…
Cancel
Save