Skip to content

Commit

Permalink
Make Collate and Like non-evaluatable
Browse files Browse the repository at this point in the history
Fixes #22317
  • Loading branch information
roji committed Aug 29, 2020
1 parent 32a0b58 commit 3d337a2
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Linq.Expressions;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Utilities;
Expand All @@ -21,6 +22,9 @@ namespace Microsoft.EntityFrameworkCore.Query
/// </summary>
public class RelationalEvaluatableExpressionFilter : EvaluatableExpressionFilter
{
private static readonly MethodInfo _collate
= typeof(RelationalDbFunctionsExtensions).GetMethod(nameof(RelationalDbFunctionsExtensions.Collate));

/// <summary>
/// <para>
/// Creates a new <see cref="RelationalEvaluatableExpressionFilter" /> instance.
Expand Down Expand Up @@ -58,13 +62,22 @@ public override bool IsEvaluatableExpression(Expression expression, IModel model
Check.NotNull(expression, nameof(expression));
Check.NotNull(model, nameof(model));

if (expression is MethodCallExpression methodCallExpression
&& model.FindDbFunction(methodCallExpression.Method) != null)
if (expression is MethodCallExpression methodCallExpression)
{
// Never evaluate DbFunction
// If it is inside lambda then we will have whole method call
// If it is outside of lambda then it will be evaluated for table valued function already.
return false;
var method = methodCallExpression.Method;

if (model.FindDbFunction(method) != null)
{
// Never evaluate DbFunction
// If it is inside lambda then we will have whole method call
// If it is outside of lambda then it will be evaluated for table valued function already.
return false;
}

if (method.IsGenericMethod && method.GetGenericMethodDefinition() == _collate)
{
return false;
}
}

return base.IsEvaluatableExpression(expression, model);
Expand Down
12 changes: 11 additions & 1 deletion src/EFCore/Query/EvaluatableExpressionFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ private static readonly MethodInfo _randomNextOneArg
private static readonly MethodInfo _randomNextTwoArgs
= typeof(Random).GetRuntimeMethod(nameof(Random.Next), new[] { typeof(int), typeof(int) });

private static readonly MethodInfo _like
= typeof(DbFunctionsExtensions).GetRuntimeMethod(
nameof(DbFunctionsExtensions.Like), new[] { typeof(DbFunctions), typeof(string), typeof(string) });

private static readonly MethodInfo _likeWithEscape
= typeof(DbFunctionsExtensions).GetRuntimeMethod(
nameof(DbFunctionsExtensions.Like), new[] { typeof(DbFunctions), typeof(string), typeof(string), typeof(string) });

/// <summary>
/// <para>
/// Creates a new <see cref="EvaluatableExpressionFilter" /> instance.
Expand Down Expand Up @@ -108,7 +116,9 @@ public virtual bool IsEvaluatableExpression(Expression expression, IModel model)
if (Equals(method, _guidNewGuid)
|| Equals(method, _randomNextNoArgs)
|| Equals(method, _randomNextOneArg)
|| Equals(method, _randomNextTwoArgs))
|| Equals(method, _randomNextTwoArgs)
|| Equals(method, _like)
|| Equals(method, _likeWithEscape))
{
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ public virtual Task Collate_case_sensitive(bool async)
c => EF.Functions.Collate(c.ContactName, CaseSensitiveCollation) == "maria anders",
c => c.ContactName.Equals("maria anders", StringComparison.Ordinal));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Collate_case_sensitive_constant(bool async)
=> AssertCount(
async,
ss => ss.Set<Customer>(),
ss => ss.Set<Customer>(),
c => c.ContactName == EF.Functions.Collate("maria anders", CaseSensitiveCollation),
c => c.ContactName.Equals("maria anders", StringComparison.Ordinal));

protected abstract string CaseInsensitiveCollation { get; }
protected abstract string CaseSensitiveCollation { get; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,26 @@ public virtual Task Like_literal_with_escape(bool async)
c => EF.Functions.Like(c.ContactName, "!%", "!"),
c => c.ContactName.Contains("%"));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Like_both_operands_literal(bool async)
=> AssertCount(
async,
ss => ss.Set<Customer>(),
ss => ss.Set<Customer>(),
c => EF.Functions.Like("FOO", "%O%"),
c => "FOO".Contains("O") || "FOO".Contains("m"));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Like_both_operands_literal_with_escape(bool async)
=> AssertCount(
async,
ss => ss.Set<Customer>(),
ss => ss.Set<Customer>(),
c => EF.Functions.Like("%", "!%", "!"),
c => "%".Contains("%"));

protected NorthwindContext CreateContext()
=> Fixture.CreateContext();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,26 @@ FROM [Customers] AS [c]
WHERE [c].[ContactName] LIKE N'!%' ESCAPE N'!'");
}

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

AssertSql(
@"SELECT COUNT(*)
FROM [Customers] AS [c]
WHERE N'FOO' LIKE N'%O%'");
}

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

AssertSql(
@"SELECT COUNT(*)
FROM [Customers] AS [c]
WHERE N'%' LIKE N'!%' ESCAPE N'!'");
}

public override async Task Collate_case_insensitive(bool async)
{
await base.Collate_case_insensitive(async);
Expand All @@ -76,6 +96,16 @@ FROM [Customers] AS [c]
WHERE [c].[ContactName] COLLATE Latin1_General_CS_AS = N'maria anders'");
}

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

AssertSql(
@"SELECT COUNT(*)
FROM [Customers] AS [c]
WHERE [c].[ContactName] = N'maria anders' COLLATE Latin1_General_CS_AS");
}

protected override string CaseInsensitiveCollation
=> "Latin1_General_CI_AI";

Expand Down

0 comments on commit 3d337a2

Please sign in to comment.