Skip to content

Commit

Permalink
implements dotnet#18845
Browse files Browse the repository at this point in the history
  • Loading branch information
scomert authored and bricelam committed Oct 30, 2020
1 parent 4699660 commit 6e5b3f8
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public SqliteMethodCallTranslatorProvider([NotNull] RelationalMethodCallTranslat
new SqliteByteArrayMethodTranslator(sqlExpressionFactory),
new SqliteDateTimeAddTranslator(sqlExpressionFactory),
new SqliteMathTranslator(sqlExpressionFactory),
new SqliteRegexTranslator(sqlExpressionFactory),
new SqliteStringMethodTranslator(sqlExpressionFactory)
});
}
Expand Down
66 changes: 66 additions & 0 deletions src/EFCore.Sqlite.Core/Query/Internal/SqliteRegexTranslator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// 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;
using System.Collections.Generic;
using System.Reflection;
using System.Text.RegularExpressions;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Sqlite.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 SqliteRegexTranslator : IMethodCallTranslator
{
private readonly ISqlExpressionFactory _sqlExpressionFactory;
private readonly static MethodInfo regexIsMatchMethod = typeof(Regex).GetMethod(nameof(Regex.IsMatch), new Type[] { typeof(string), typeof(string) });

/// <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 SqliteRegexTranslator([NotNull] ISqlExpressionFactory sqlExpressionFactory)
{
_sqlExpressionFactory = sqlExpressionFactory;
}
/// <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 SqlExpression Translate(SqlExpression instance,
MethodInfo method,
IReadOnlyList<SqlExpression> arguments,
IDiagnosticsLogger<DbLoggerCategory.Query> logger)
{
Check.NotNull(method, nameof(method));
Check.NotNull(arguments, nameof(arguments));
Check.NotNull(logger, nameof(logger));


if (method.Equals(regexIsMatchMethod))
{
return _sqlExpressionFactory.Function("regexp",
new[] { arguments[1], arguments[0] },
false,
new[] { false, false },
typeof(bool),
arguments[0].TypeMapping);
}

return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Data.Common;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using JetBrains.Annotations;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore.Diagnostics;
Expand Down Expand Up @@ -114,6 +115,19 @@ private void InitializeDbConnection(DbConnection connection)
sqliteConnection.DefaultTimeout = _commandTimeout.Value;
}

sqliteConnection.CreateFunction<string, string, bool>(
"regexp",
(pattern, input) =>
{
if (input == null || pattern == null)
{
return false;
}
return Regex.IsMatch(input, pattern);
},
isDeterministic: true);

sqliteConnection.CreateFunction<object, object, object>(
"ef_mod",
(dividend, divisor) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.TestModels.Northwind;
using Microsoft.EntityFrameworkCore.TestUtilities;
Expand Down Expand Up @@ -1584,5 +1585,14 @@ public virtual Task Projecting_Math_Truncate_and_ordering_by_it_twice3(bool asyn
.ThenBy(r => r.A),
assertOrder: true);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Regex_IsMatch_MethodCall(bool async)
{
return AssertQuery(async,
ss => ss.Set<Customer>().Where(o => Regex.IsMatch(o.CustomerID, @"^T")),
entryCount: 6);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1548,6 +1548,10 @@ public override async Task Projecting_Math_Truncate_and_ordering_by_it_twice3(bo
//WHERE [o].[OrderID] < 10250
//ORDER BY [A] DESC");
}
public override Task Regex_IsMatch_MethodCall(bool async)
{
return AssertTranslationFailed(() => base.Regex_IsMatch_MethodCall(async));
}

private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,15 @@ public override async Task Trim_with_char_array_argument_in_predicate(bool async
WHERE trim(""c"".""ContactTitle"", 'Or') = 'wne'");
}

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

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""
WHERE regexp('^T', ""c"".""CustomerID"")");
}

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

0 comments on commit 6e5b3f8

Please sign in to comment.