Skip to content

Commit

Permalink
Rename Cosmos MethodCallTranslators. Support multiple RegexOptions
Browse files Browse the repository at this point in the history
  • Loading branch information
Marusyk committed May 29, 2022
1 parent b8cba0c commit 1127cab
Show file tree
Hide file tree
Showing 8 changed files with 237 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.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.
/// </summary>
public class ContainsTranslator : IMethodCallTranslator
public class CosmosContainsTranslator : IMethodCallTranslator
{
private readonly ISqlExpressionFactory _sqlExpressionFactory;

Expand All @@ -19,7 +19,7 @@ public class ContainsTranslator : 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.
/// </summary>
public ContainsTranslator(ISqlExpressionFactory sqlExpressionFactory)
public CosmosContainsTranslator(ISqlExpressionFactory sqlExpressionFactory)
{
_sqlExpressionFactory = sqlExpressionFactory;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.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.
/// </summary>
public class EqualsTranslator : IMethodCallTranslator
public class CosmosEqualsTranslator : IMethodCallTranslator
{
private readonly ISqlExpressionFactory _sqlExpressionFactory;

Expand All @@ -19,7 +19,7 @@ public class EqualsTranslator : 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.
/// </summary>
public EqualsTranslator(ISqlExpressionFactory sqlExpressionFactory)
public CosmosEqualsTranslator(ISqlExpressionFactory sqlExpressionFactory)
{
_sqlExpressionFactory = sqlExpressionFactory;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.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.
/// </summary>
public class MathTranslator : IMethodCallTranslator
public class CosmosMathTranslator : IMethodCallTranslator
{
private static readonly Dictionary<MethodInfo, string> SupportedMethodTranslations = new()
{
Expand Down Expand Up @@ -77,7 +77,7 @@ public class MathTranslator : 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.
/// </summary>
public MathTranslator(ISqlExpressionFactory sqlExpressionFactory)
public CosmosMathTranslator(ISqlExpressionFactory sqlExpressionFactory)
{
_sqlExpressionFactory = sqlExpressionFactory;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ public CosmosMethodCallTranslatorProvider(
_translators.AddRange(
new IMethodCallTranslator[]
{
new EqualsTranslator(sqlExpressionFactory),
new CosmosEqualsTranslator(sqlExpressionFactory),
new CosmosStringMethodTranslator(sqlExpressionFactory),
new ContainsTranslator(sqlExpressionFactory),
new RandomTranslator(sqlExpressionFactory),
new MathTranslator(sqlExpressionFactory),
new RegexMethodTranslator(sqlExpressionFactory)
new CosmosContainsTranslator(sqlExpressionFactory),
new CosmosRandomTranslator(sqlExpressionFactory),
new CosmosMathTranslator(sqlExpressionFactory),
new CosmosRegexTranslator(sqlExpressionFactory)
//new LikeTranslator(sqlExpressionFactory),
//new EnumHasFlagTranslator(sqlExpressionFactory),
//new GetValueOrDefaultTranslator(sqlExpressionFactory),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.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.
/// </summary>
public class RandomTranslator : IMethodCallTranslator
public class CosmosRandomTranslator : IMethodCallTranslator
{
private static readonly MethodInfo MethodInfo = typeof(DbFunctionsExtensions).GetRuntimeMethod(
nameof(DbFunctionsExtensions.Random), new[] { typeof(DbFunctions) });
Expand All @@ -24,7 +24,7 @@ public class RandomTranslator : 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.
/// </summary>
public RandomTranslator(ISqlExpressionFactory sqlExpressionFactory)
public CosmosRandomTranslator(ISqlExpressionFactory sqlExpressionFactory)
{
_sqlExpressionFactory = sqlExpressionFactory;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.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.
/// </summary>
public class RegexMethodTranslator : IMethodCallTranslator
public class CosmosRegexTranslator : IMethodCallTranslator
{
private static readonly MethodInfo IsMatch =
typeof(Regex).GetRuntimeMethod(nameof(Regex.IsMatch), new[] { typeof(string), typeof(string) })!;
Expand All @@ -36,7 +36,7 @@ public class RegexMethodTranslator : 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.
/// </summary>
public RegexMethodTranslator(ISqlExpressionFactory sqlExpressionFactory)
public CosmosRegexTranslator(ISqlExpressionFactory sqlExpressionFactory)
{
_sqlExpressionFactory = sqlExpressionFactory;
}
Expand All @@ -58,37 +58,49 @@ public RegexMethodTranslator(ISqlExpressionFactory sqlExpressionFactory)
return null;
}

RegexOptions options;
var (input, pattern) = (arguments[0], arguments[1]);
var typeMapping = ExpressionExtensions.InferTypeMapping(input, pattern);

if (method == IsMatch)
{
options = RegexOptions.None;
return _sqlExpressionFactory.Function(
"RegexMatch",
new[] {
_sqlExpressionFactory.ApplyTypeMapping(input, typeMapping),
_sqlExpressionFactory.ApplyTypeMapping(input, typeMapping)
},
typeof(bool));
}
else if (arguments[2] is SqlConstantExpression { Value: RegexOptions regexOptions }
&& AllowedOptions.Contains(regexOptions))
else if (arguments[2] is SqlConstantExpression { Value: RegexOptions regexOptions })
{
options = regexOptions;
}
else
{
return null;
}
string modifier = "";
foreach (RegexOptions option in Enum.GetValues<RegexOptions>().Where(o => regexOptions.HasFlag(o)))
{
if (!AllowedOptions.Contains(option))
{
return null;
}

string modifier = options switch
{
RegexOptions.Multiline => "m",
RegexOptions.Singleline => "s",
RegexOptions.IgnoreCase => "i",
RegexOptions.IgnorePatternWhitespace => "x",
_ => ""
};
modifier += option switch
{
RegexOptions.Multiline => "m",
RegexOptions.Singleline => "s",
RegexOptions.IgnoreCase => "i",
RegexOptions.IgnorePatternWhitespace => "x",
_ => ""
};
}

var (input, pattern) = (arguments[0], arguments[1]);
var stringTypeMapping = ExpressionExtensions.InferTypeMapping(input, pattern);
return _sqlExpressionFactory.Function(
"RegexMatch",
new[] {
_sqlExpressionFactory.ApplyTypeMapping(input, typeMapping),
_sqlExpressionFactory.ApplyTypeMapping(pattern, typeMapping),
_sqlExpressionFactory.Constant(modifier, typeMapping)
},
typeof(bool));
}

return _sqlExpressionFactory.Function(
"RegexMatch",
new[] { input, pattern, _sqlExpressionFactory.Constant(modifier) },
method.ReturnType,
stringTypeMapping);
return null;
}
}
162 changes: 162 additions & 0 deletions src/EFCore.Cosmos/Query/Internal/SqlRegexMatchExpression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text.RegularExpressions;
using Microsoft.EntityFrameworkCore.Cosmos.Internal;

namespace Microsoft.EntityFrameworkCore.Cosmos.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 SqlRegexMatchExpression : SqlExpression, IEquatable<SqlRegexMatchExpression>
{
private static readonly ISet<RegexOptions> AllowedOptions = new HashSet<RegexOptions>
{
RegexOptions.None,
RegexOptions.IgnoreCase,
RegexOptions.Multiline,
RegexOptions.Singleline,
RegexOptions.IgnorePatternWhitespace
};

private static RegexOptions VerifyOptions(RegexOptions optionType)
=> AllowedOptions.Contains(optionType)
? optionType
: throw new InvalidOperationException(
CosmosStrings.UnsupportedOperatorForSqlExpression(
optionType, typeof(SqlRegexMatchExpression).ShortDisplayName()));

/// <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 SqlRegexMatchExpression(
SqlExpression match,
SqlExpression pattern,
RegexOptions options,
CoreTypeMapping? typeMapping)
: base(typeof(bool), typeMapping)
{
Match = match;
Pattern = pattern;

Options = VerifyOptions(options);
}

/// <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 Match { get; }

/// <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 Pattern { get; }

/// <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 RegexOptions Options { get; }

/// <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>
protected override Expression VisitChildren(ExpressionVisitor visitor)
=> Update((SqlExpression)visitor.Visit(Match), (SqlExpression)visitor.Visit(Pattern));

/// <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 SqlRegexMatchExpression Update(SqlExpression match, SqlExpression pattern)
=> match != Match || pattern != Pattern
? new SqlRegexMatchExpression(match, pattern, Options, TypeMapping)
: this;

/// <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 bool Equals(SqlRegexMatchExpression? other)
=> ReferenceEquals(this, other)
|| other is not null
&& base.Equals(other)
&& Match.Equals(other.Match)
&& Pattern.Equals(other.Pattern)
&& Options.Equals(other.Options);

/// <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 override bool Equals(object? other)
=> other is SqlRegexMatchExpression otherRegexMatch && Equals(otherRegexMatch);

/// <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 override int GetHashCode()
=> HashCode.Combine(base.GetHashCode(), Match, Pattern, Options);

/// <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>
protected override void Print(ExpressionPrinter expressionPrinter)
{
expressionPrinter.Append("RegexMatch(");
expressionPrinter.Visit(Match);
expressionPrinter.Append(", ");
expressionPrinter.Visit(Pattern);
expressionPrinter.Append(", ");

var option = Options switch
{
RegexOptions.Multiline => "m",
RegexOptions.Singleline => "s",
RegexOptions.IgnoreCase => "i",
RegexOptions.IgnorePatternWhitespace => "x",
_ => ""
};

expressionPrinter.Append(option);
expressionPrinter.Append(")");
}

///// <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 override string ToString() => $"RegexMatch({Match}, {Pattern}, {Options})";
}
Loading

0 comments on commit 1127cab

Please sign in to comment.