Repository: ignite Updated Branches: refs/heads/master a6e28082b -> 5e1a22db1
IGNITE-4723 .NET: Support REGEXP_LIKE in LINQ This closes #2842 Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/5e1a22db Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/5e1a22db Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/5e1a22db Branch: refs/heads/master Commit: 5e1a22db1c1da0e5d866fd5a465dd3cdc7b3ffa0 Parents: a6e2808 Author: Alexey Popov <[email protected]> Authored: Fri Oct 13 14:19:14 2017 +0300 Committer: Pavel Tupitsyn <[email protected]> Committed: Fri Oct 13 14:19:14 2017 +0300 ---------------------------------------------------------------------- .../Cache/Query/Linq/CacheLinqTest.Strings.cs | 18 +++++++ .../Impl/CacheQueryExpressionVisitor.cs | 5 ++ .../Apache.Ignite.Linq/Impl/MethodVisitor.cs | 55 +++++++++++++++++++- 3 files changed, 76 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/5e1a22db/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Strings.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Strings.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Strings.cs index 1139c4d..35996b0 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Strings.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Strings.cs @@ -83,6 +83,24 @@ namespace Apache.Ignite.Core.Tests.Cache.Query.Linq Assert.Throws<NotSupportedException>(() => CheckFunc(x => x.TrimEnd(toTrimFails), strings)); CheckFunc(x => Regex.Replace(x, @"son.\d", "kele!"), strings); + CheckFunc(x => Regex.Replace(x, @"son.\d", "kele!", RegexOptions.None), strings); + CheckFunc(x => Regex.Replace(x, @"person.\d", "akele!", RegexOptions.IgnoreCase), strings); + CheckFunc(x => Regex.Replace(x, @"person.\d", "akele!", RegexOptions.Multiline), strings); + CheckFunc(x => Regex.Replace(x, @"person.\d", "akele!", RegexOptions.IgnoreCase | RegexOptions.Multiline), + strings); + var notSupportedException = Assert.Throws<NotSupportedException>(() => CheckFunc(x => + Regex.IsMatch(x, @"^person\d", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant), strings)); + Assert.AreEqual("RegexOptions.CultureInvariant is not supported", notSupportedException.Message); + + CheckFunc(x => Regex.IsMatch(x, @"^Person_9\d"), strings); + CheckFunc(x => Regex.IsMatch(x, @"^person_9\d", RegexOptions.None), strings); + CheckFunc(x => Regex.IsMatch(x, @"^person_9\d", RegexOptions.IgnoreCase), strings); + CheckFunc(x => Regex.IsMatch(x, @"^Person_9\d", RegexOptions.Multiline), strings); + CheckFunc(x => Regex.IsMatch(x, @"^person_9\d", RegexOptions.IgnoreCase | RegexOptions.Multiline), strings); + notSupportedException = Assert.Throws<NotSupportedException>(() => CheckFunc(x => + Regex.IsMatch(x, @"^person_9\d",RegexOptions.IgnoreCase | RegexOptions.CultureInvariant), strings)); + Assert.AreEqual("RegexOptions.CultureInvariant is not supported", notSupportedException.Message); + CheckFunc(x => x.Replace("son", ""), strings); CheckFunc(x => x.Replace("son", "kele"), strings); http://git-wip-us.apache.org/repos/asf/ignite/blob/5e1a22db/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs index d187f08..4caefe1 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs @@ -474,6 +474,11 @@ namespace Apache.Ignite.Linq.Impl [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")] protected override Expression VisitConstant(ConstantExpression expression) { + if (MethodVisitor.VisitConstantCall(expression, this)) + { + return expression; + } + AppendParameter(expression.Value); return expression; http://git-wip-us.apache.org/repos/asf/ignite/blob/5e1a22db/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/MethodVisitor.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/MethodVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/MethodVisitor.cs index 8abf2a6..84bd98f 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/MethodVisitor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/MethodVisitor.cs @@ -71,8 +71,12 @@ namespace Apache.Ignite.Linq.Impl GetStringMethod("PadRight", "rpad", typeof (int)), GetStringMethod("PadRight", "rpad", typeof (int), typeof (char)), - GetMethod(typeof (Regex), "Replace", new[] {typeof (string), typeof (string), typeof (string)}, - GetFunc("regexp_replace")), + GetRegexMethod("Replace", "regexp_replace", typeof (string), typeof (string), typeof (string)), + GetRegexMethod("Replace", "regexp_replace", typeof (string), typeof (string), typeof (string), + typeof(RegexOptions)), + GetRegexMethod("IsMatch", "regexp_like", typeof (string), typeof (string)), + GetRegexMethod("IsMatch", "regexp_like", typeof (string), typeof (string), typeof(RegexOptions)), + GetMethod(typeof (DateTime), "ToString", new[] {typeof (string)}, (e, v) => VisitFunc(e, v, "formatdatetime", ", 'en', 'UTC'")), @@ -117,6 +121,13 @@ namespace Apache.Ignite.Linq.Impl GetMathMethod("Truncate", typeof (decimal)), }.ToDictionary(x => x.Key, x => x.Value); + /// <summary> RegexOptions transformations. </summary> + private static readonly Dictionary<RegexOptions, string> RegexOptionFlags = new Dictionary<RegexOptions, string> + { + { RegexOptions.IgnoreCase, "i" }, + { RegexOptions.Multiline, "m" } + }; + /// <summary> /// Visits the property call expression. /// </summary> @@ -153,6 +164,37 @@ namespace Apache.Ignite.Linq.Impl } /// <summary> + /// Visits the constant call expression. + /// </summary> + public static bool VisitConstantCall(ConstantExpression expression, CacheQueryExpressionVisitor visitor) + { + if (expression.Type != typeof(RegexOptions)) + { + return false; + } + + var regexOptions = expression.Value as RegexOptions? ?? RegexOptions.None; + var result = string.Empty; + foreach (var option in RegexOptionFlags) + { + if (regexOptions.HasFlag(option.Key)) + { + result += option.Value; + regexOptions &= ~option.Key; + } + } + + if (regexOptions != RegexOptions.None) + { + throw new NotSupportedException(string.Format("RegexOptions.{0} is not supported", regexOptions)); + } + + visitor.AppendParameter(result); + + return true; + } + + /// <summary> /// Gets the function. /// </summary> private static VisitMethodDelegate GetFunc(string func, params int[] adjust) @@ -302,6 +344,15 @@ namespace Apache.Ignite.Linq.Impl } /// <summary> + /// Gets the Regex method. + /// </summary> + private static KeyValuePair<MethodInfo, VisitMethodDelegate> GetRegexMethod(string name, string sqlName, + params Type[] argTypes) + { + return GetMethod(typeof(Regex), name, argTypes, GetFunc(sqlName)); + } + + /// <summary> /// Gets string parameterized Trim(TrimStart, TrimEnd) method. /// </summary> private static KeyValuePair<MethodInfo, VisitMethodDelegate> GetParameterizedTrimMethod(string name,
