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.
var parameters = InnerCommand.Parameters.Cast<DbParameter>()
.ToList();
if (parameters.Count > 0)
{
var lastCommandText = InnerCommand.CommandText;
var commandText = lastCommandText;
if (parameters.Count > 0)
{
while ((commandText = _topMultiParameterRegularExpression.Replace(
lastCommandText,
match =>
@ -587,6 +586,9 @@ namespace EntityFrameworkCore.Jet.Data
lastCommandText = commandText;
}
InnerCommand.Parameters.Clear();
InnerCommand.Parameters.AddRange(parameters.ToArray());
}
while ((commandText = _topMultiArgumentRegularExpression.Replace(
lastCommandText,
match =>
@ -601,10 +603,6 @@ namespace EntityFrameworkCore.Jet.Data
}
InnerCommand.CommandText = commandText;
InnerCommand.Parameters.Clear();
InnerCommand.Parameters.AddRange(parameters.ToArray());
}
}
private void ModifyOuterSelectTopValueForOuterSelectSkipEmulationViaDataReader()

@ -40,19 +40,19 @@ public class JetParameterBasedSqlProcessor : RelationalParameterBasedSqlProcesso
IReadOnlyDictionary<string, object?> parametersValues,
out bool canCache)
{
queryExpression = base.Optimize(queryExpression, parametersValues, out canCache);
var optimizedQueryExpression = base.Optimize(queryExpression, parametersValues, out canCache);
/*optimizedQueryExpression = new SkipTakeCollapsingExpressionVisitor(Dependencies.SqlExpressionFactory)
.Process(optimizedQueryExpression, parametersValues, out var canCache2);*/
optimizedQueryExpression = new SkipTakeCollapsingExpressionVisitor(Dependencies.SqlExpressionFactory)
.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.
queryExpression = new JetCompatibilityExpressionVisitor().Visit(queryExpression);
optimizedQueryExpression = new JetCompatibilityExpressionVisitor().Visit(optimizedQueryExpression);
return queryExpression;
return optimizedQueryExpression;
}
/// <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_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_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: True)
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: True)
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: True)
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_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_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: True)
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: True)
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: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindMiscellaneousQueryJetTest.Skip_Take_Any_with_predicate(isAsync: False)

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

@ -5465,18 +5465,31 @@ CROSS APPLY (
AssertSql(
"""
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]
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 `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 [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]
ORDER BY [c].[CustomerID]
OFFSET @__p_0 ROWS FETCH NEXT @__p_0 ROWS ONLY
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 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 (
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`
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(
"""
SELECT CASE
WHEN EXISTS (
SELECT IIF(EXISTS (
SELECT 1
FROM [Orders] AS [o]
WHERE 0 = 1) THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END
FROM [Customers] AS [c]
WHERE [c].[CustomerID] LIKE N'F%'
ORDER BY [c].[CustomerID]
FROM (
SELECT `t`.`OrderID`, `t`.`CustomerID`, `t`.`EmployeeID`, `t`.`OrderDate`
FROM (
SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
WHERE 0 = 1
) 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