diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteMethodCallTranslatorProvider.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteMethodCallTranslatorProvider.cs index f35640cacc7..9f1660c5a0c 100644 --- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteMethodCallTranslatorProvider.cs +++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteMethodCallTranslatorProvider.cs @@ -31,7 +31,7 @@ public SqliteMethodCallTranslatorProvider([NotNull] RelationalMethodCallTranslat new SqliteByteArrayMethodTranslator(sqlExpressionFactory), new SqliteDateTimeAddTranslator(sqlExpressionFactory), new SqliteMathTranslator(sqlExpressionFactory), - new SqliteRegexTranslator(sqlExpressionFactory), + new SqliteRegexMethodTranslator(sqlExpressionFactory), new SqliteStringMethodTranslator(sqlExpressionFactory) }); } diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteRegexTranslator.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteRegexMethodTranslator.cs similarity index 78% rename from src/EFCore.Sqlite.Core/Query/Internal/SqliteRegexTranslator.cs rename to src/EFCore.Sqlite.Core/Query/Internal/SqliteRegexMethodTranslator.cs index 8fee6f50889..29ed462f610 100644 --- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteRegexTranslator.cs +++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteRegexMethodTranslator.cs @@ -19,10 +19,12 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Query.Internal /// 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. /// - public class SqliteRegexTranslator : IMethodCallTranslator + public class SqliteRegexMethodTranslator : IMethodCallTranslator { + private readonly static MethodInfo _regexIsMatchMethodInfo + = typeof(Regex).GetRuntimeMethod(nameof(Regex.IsMatch), new Type[] { typeof(string), typeof(string) }); + private readonly ISqlExpressionFactory _sqlExpressionFactory; - private readonly static MethodInfo regexIsMatchMethod = typeof(Regex).GetMethod(nameof(Regex.IsMatch), new Type[] { typeof(string), typeof(string) }); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -30,17 +32,21 @@ public class SqliteRegexTranslator : IMethodCallTranslator /// 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. /// - public SqliteRegexTranslator([NotNull] ISqlExpressionFactory sqlExpressionFactory) + public SqliteRegexMethodTranslator([NotNull] ISqlExpressionFactory sqlExpressionFactory) { + Check.NotNull(sqlExpressionFactory, nameof(sqlExpressionFactory)); + _sqlExpressionFactory = sqlExpressionFactory; } + /// /// 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. /// - public virtual SqlExpression Translate(SqlExpression instance, + public virtual SqlExpression Translate( + SqlExpression instance, MethodInfo method, IReadOnlyList arguments, IDiagnosticsLogger logger) @@ -49,13 +55,13 @@ public virtual SqlExpression Translate(SqlExpression instance, Check.NotNull(arguments, nameof(arguments)); Check.NotNull(logger, nameof(logger)); - - if (method.Equals(regexIsMatchMethod)) + if (method.Equals(_regexIsMatchMethodInfo)) { - return _sqlExpressionFactory.Function("regexp", + return _sqlExpressionFactory.Function( + "regexp", new[] { arguments[1], arguments[0] }, - false, - new[] { false, false }, + nullable: true, + argumentsPropagateNullability: new[] { true, true }, typeof(bool), arguments[0].TypeMapping); } diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteRelationalConnection.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteRelationalConnection.cs index a78fce5a056..1d32e86e7fc 100644 --- a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteRelationalConnection.cs +++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteRelationalConnection.cs @@ -115,13 +115,14 @@ private void InitializeDbConnection(DbConnection connection) sqliteConnection.DefaultTimeout = _commandTimeout.Value; } - sqliteConnection.CreateFunction( + sqliteConnection.CreateFunction( "regexp", (pattern, input) => { - if (input == null || pattern == null) + if (input == null + || pattern == null) { - return false; + return null; } return Regex.IsMatch(input, pattern); @@ -146,7 +147,8 @@ private void InitializeDbConnection(DbConnection connection) return Convert.ToDouble(dividend, CultureInfo.InvariantCulture) % Convert.ToDouble(divisor, CultureInfo.InvariantCulture); - }); + }, + isDeterministic: true); sqliteConnection.CreateFunction( name: "ef_add", @@ -176,7 +178,6 @@ private void InitializeDbConnection(DbConnection connection) isDeterministic: true); } else - { _logger.UnexpectedConnectionTypeWarning(connection.GetType()); } diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindFunctionsQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindFunctionsQueryCosmosTest.cs index 5937253d50f..5452c1928f8 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindFunctionsQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindFunctionsQueryCosmosTest.cs @@ -1049,6 +1049,11 @@ public override Task Int_Compare_to_simple_zero(bool async) return base.Int_Compare_to_simple_zero(async); } + 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); diff --git a/test/EFCore.Specification.Tests/Query/NorthwindFunctionsQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindFunctionsQueryTestBase.cs index 52788059cd2..57b86da8d3f 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindFunctionsQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindFunctionsQueryTestBase.cs @@ -1590,7 +1590,8 @@ public virtual Task Projecting_Math_Truncate_and_ordering_by_it_twice3(bool asyn [MemberData(nameof(IsAsyncData))] public virtual Task Regex_IsMatch_MethodCall(bool async) { - return AssertQuery(async, + return AssertQuery( + async, ss => ss.Set().Where(o => Regex.IsMatch(o.CustomerID, @"^T")), entryCount: 6); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindFunctionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindFunctionsQuerySqlServerTest.cs index 130c6256bbc..630e0e6b822 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindFunctionsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindFunctionsQuerySqlServerTest.cs @@ -1548,6 +1548,7 @@ 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));