Repository: ignite Updated Branches: refs/heads/ignite-2.3 6df7ebc43 -> b00a05c62
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/b00a05c6 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/b00a05c6 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/b00a05c6 Branch: refs/heads/ignite-2.3 Commit: b00a05c621ba8200536484257400a6e4dcf30086 Parents: 6df7ebc Author: Alexey Popov <[email protected]> Authored: Fri Oct 13 14:19:14 2017 +0300 Committer: Pavel Tupitsyn <[email protected]> Committed: Fri Oct 13 17:24:19 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/b00a05c6/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 7457d0a..b2bcfdd 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 @@ -78,6 +78,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/b00a05c6/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/b00a05c6/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 9446af3..054a984 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/MethodVisitor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/MethodVisitor.cs @@ -67,8 +67,12 @@ namespace Apache.Ignite.Linq.Impl GetParameterizedTrimMethod("TrimEnd", "rtrim"), GetStringMethod("Replace", "replace", typeof(string), typeof(string)), - 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'")), @@ -113,6 +117,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> @@ -149,6 +160,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) @@ -298,6 +340,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,
