Skip to content

Commit

Permalink
Add expression visitor to ignore checked expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
svengeance committed Jan 15, 2020
1 parent 68783e1 commit ccb3833
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 0 deletions.
39 changes: 39 additions & 0 deletions src/EFCore/Query/IgnoreCheckedExpressionVisitor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Linq.Expressions;

namespace Microsoft.EntityFrameworkCore.Query
{
public class IgnoreCheckedExpressionVisitor: ExpressionVisitor
{
protected override Expression VisitBinary(BinaryExpression node)
{
var visitedLeft = Visit(node.Left);
var visitedRight = Visit(node.Right);

var visitBinary = node.NodeType switch
{
ExpressionType.AddChecked => Expression.Add(visitedLeft, visitedRight),
ExpressionType.SubtractChecked => Expression.Subtract(visitedLeft, visitedRight),
ExpressionType.MultiplyChecked => Expression.Multiply(visitedLeft, visitedRight),
_ => base.VisitBinary(node)
};

return visitBinary;
}

protected override Expression VisitUnary(UnaryExpression node)
{
var operand = Visit(node.Operand);

var visitUnary = node.NodeType switch
{
ExpressionType.ConvertChecked => Expression.Convert(operand, node.Type),
_ => base.VisitUnary(node)
};

return visitUnary;
}
}
}
1 change: 1 addition & 0 deletions src/EFCore/Query/QueryTranslationPreprocessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public virtual Expression Process([NotNull] Expression query)
{
Check.NotNull(query, nameof(query));

query = new IgnoreCheckedExpressionVisitor().Visit(query);
query = new EnumerableToQueryableMethodConvertingExpressionVisitor().Visit(query);
query = new QueryMetadataExtractingExpressionVisitor(_queryCompilationContext).Visit(query);
query = new InvocationExpressionRemovingExpressionVisitor().Visit(query);
Expand Down
28 changes: 28 additions & 0 deletions test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7518,6 +7518,34 @@ public virtual Task Indexer_property_is_pushdown_into_subquery(bool isAsync)
ss => ss.Set<Gear>().Where(g => ss.Set<City>().Where(c => c.Name == g.CityOfBirthName).FirstOrDefault()["Nation"] == null));
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Checked_context_with_cast_does_not_fail(bool isAsync)
{
using var ctx = CreateContext();

checked
{
return AssertQuery(
isAsync,
ss => ss.Set<LocustLeader>().Where(w => (byte)w.ThreatLevel >= (short?)5));
}
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Checked_context_with_addition_does_not_fail(bool isAsync)
{
using var ctx = CreateContext();

checked
{
return AssertQuery(
isAsync,
ss => ss.Set<LocustLeader>().Where(w => w.ThreatLevel >= ((int)(long?)5 + (long?)w.ThreatLevel)));
}
}

protected GearsOfWarContext CreateContext() => Fixture.CreateContext();

protected virtual void ClearLog()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7495,6 +7495,26 @@ FROM [Cities] AS [c]
WHERE [c].[Name] = [g].[CityOfBirthName]) IS NULL");
}

public override async Task Checked_context_with_cast_does_not_fail(bool isAsync)
{
await base.Checked_context_with_cast_does_not_fail(isAsync);

AssertSql(
@"SELECT [l].[Name], [l].[Discriminator], [l].[LocustHordeId], [l].[ThreatLevel], [l].[DefeatedByNickname], [l].[DefeatedBySquadId], [l].[HighCommandId]
FROM [LocustLeaders] AS [l]
WHERE [l].[Discriminator] IN (N'LocustLeader', N'LocustCommander') AND (CAST([l].[ThreatLevel] AS tinyint) >= CAST(5 AS tinyint))");
}

public override async Task Checked_context_with_addition_does_not_fail(bool isAsync)
{
await base.Checked_context_with_addition_does_not_fail(isAsync);

AssertSql(
@"SELECT [l].[Name], [l].[Discriminator], [l].[LocustHordeId], [l].[ThreatLevel], [l].[DefeatedByNickname], [l].[DefeatedBySquadId], [l].[HighCommandId]
FROM [LocustLeaders] AS [l]
WHERE [l].[Discriminator] IN (N'LocustLeader', N'LocustCommander') AND (CAST([l].[ThreatLevel] AS bigint) >= (CAST(5 AS bigint) + CAST([l].[ThreatLevel] AS bigint)))");
}

private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,26 @@ SELECT COUNT(*)
WHERE (length(""s"".""Banner"") = length(@__byteArrayParam)) OR (length(""s"".""Banner"") IS NULL AND length(@__byteArrayParam) IS NULL)");
}

public override async Task Checked_context_with_cast_does_not_fail(bool isAsync)
{
await base.Checked_context_with_cast_does_not_fail(isAsync);

AssertSql(
@"SELECT ""l"".""Name"", ""l"".""Discriminator"", ""l"".""LocustHordeId"", ""l"".""ThreatLevel"", ""l"".""DefeatedByNickname"", ""l"".""DefeatedBySquadId"", ""l"".""HighCommandId""
FROM ""LocustLeaders"" AS ""l""
WHERE ""l"".""Discriminator"" IN ('LocustLeader', 'LocustCommander') AND (CAST(""l"".""ThreatLevel"" AS INTEGER) >= 5)");
}

public override async Task Checked_context_with_addition_does_not_fail(bool isAsync)
{
await base.Checked_context_with_addition_does_not_fail(isAsync);

AssertSql(
@"SELECT ""l"".""Name"", ""l"".""Discriminator"", ""l"".""LocustHordeId"", ""l"".""ThreatLevel"", ""l"".""DefeatedByNickname"", ""l"".""DefeatedBySquadId"", ""l"".""HighCommandId""
FROM ""LocustLeaders"" AS ""l""
WHERE ""l"".""Discriminator"" IN ('LocustLeader', 'LocustCommander') AND (CAST(""l"".""ThreatLevel"" AS INTEGER) >= (5 + CAST(""l"".""ThreatLevel"" AS INTEGER)))");
}

private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
}
Expand Down

0 comments on commit ccb3833

Please sign in to comment.