Skip to content

Commit 855ef39

Browse files
committed
Pre-transform like arguments so the db can reuse the query plan
Fixes nhibernate#725
1 parent 20a61d1 commit 855ef39

File tree

5 files changed

+98
-26
lines changed

5 files changed

+98
-26
lines changed

.editorconfig

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,45 @@
11
root=true
22

33
[*]
4-
insert_final_newline = true
4+
insert_final_newline=true
55

66
[*.cs]
7-
indent_style = tab
8-
dotnet_sort_system_directives_first = true
9-
csharp_space_after_cast = true
10-
csharp_new_line_before_open_brace = all
11-
csharp_new_line_before_else = true
12-
csharp_new_line_before_catch = true
13-
csharp_new_line_before_finally = true
14-
csharp_new_line_before_members_in_object_initializers = true
15-
csharp_new_line_before_members_in_anonymous_types = true
16-
csharp_new_line_between_query_expression_clauses = true
7+
indent_style=tab
8+
dotnet_sort_system_directives_first=true
9+
csharp_space_after_cast=true
10+
csharp_new_line_before_open_brace=all
11+
csharp_new_line_before_else=true
12+
csharp_new_line_before_catch=true
13+
csharp_new_line_before_finally=true
14+
csharp_new_line_before_members_in_object_initializers=true
15+
csharp_new_line_before_members_in_anonymous_types=true
16+
csharp_new_line_between_query_expression_clauses=true
17+
18+
# ReSharper properties
19+
resharper_space_within_single_line_array_initializer_braces=true
1720

1821
[*.xsd]
19-
indent_style = tab
22+
indent_style=tab
2023

2124
[*.json]
22-
indent_style = space
23-
indent_size = 2
25+
indent_style=space
26+
indent_size=2
2427

2528
[*.xml]
26-
indent_style = space
27-
indent_size = 2
29+
indent_style=space
30+
indent_size=2
2831

2932
[*.csproj]
30-
indent_style = space
31-
indent_size = 2
33+
indent_style=space
34+
indent_size=2
3235

3336
[*.vbproj]
34-
indent_style = space
35-
indent_size = 2
37+
indent_style=space
38+
indent_size=2
3639

3740
[*.cshtml]
38-
indent_style = space
39-
indent_size = 4
41+
indent_style=space
42+
indent_size=4
4043

4144
[*.g]
42-
indent_style = tab
45+
indent_style=tab
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using System.Linq.Expressions;
2+
using System.Reflection;
3+
using NHibernate.Util;
4+
using Remotion.Linq.Parsing.ExpressionVisitors.Transformation;
5+
6+
namespace NHibernate.Linq.ExpressionTransformers
7+
{
8+
/// <summary>
9+
/// Replace <see cref="string.StartsWith(string)"/>, <see cref="string.EndsWith(string)"/> and <see cref="string.Contains(string)"/>
10+
/// with <see cref="SqlMethods.Like(string, string)"/>
11+
/// </summary>
12+
internal class LikeTransformer : IExpressionTransformer<MethodCallExpression>
13+
{
14+
private static readonly MethodInfo Like = ReflectHelper.FastGetMethod(SqlMethods.Like, default(string), default(string));
15+
16+
public ExpressionType[] SupportedExpressionTypes { get; } = {ExpressionType.Call};
17+
18+
public Expression Transform(MethodCallExpression expression)
19+
{
20+
if (expression.Method == ReflectionCache.StringMethods.StartsWith)
21+
{
22+
return Expression.Call(
23+
Like,
24+
expression.Object,
25+
Concat(expression.Arguments[0], Expression.Constant("%"))
26+
);
27+
}
28+
29+
if (expression.Method == ReflectionCache.StringMethods.EndsWith)
30+
{
31+
return Expression.Call(
32+
Like,
33+
expression.Object,
34+
Concat(Expression.Constant("%"), expression.Arguments[0])
35+
);
36+
}
37+
38+
if (expression.Method == ReflectionCache.StringMethods.Contains)
39+
{
40+
return Expression.Call(
41+
Like,
42+
expression.Object,
43+
Concat(Concat(Expression.Constant("%"), expression.Arguments[0]), Expression.Constant("%"))
44+
);
45+
}
46+
47+
return expression;
48+
}
49+
50+
private static Expression Concat(Expression arg1, Expression arg2)
51+
{
52+
if (arg1 is ConstantExpression const1 && arg2 is ConstantExpression const2)
53+
{
54+
return Expression.Constant(string.Concat(const1.Value, const2.Value));
55+
}
56+
57+
return Expression.Add(arg1, arg2, ReflectionCache.StringMethods.Concat);
58+
}
59+
}
60+
}

src/NHibernate/Linq/Functions/StringGenerator.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public class StartsWithGenerator : BaseHqlGeneratorForMethod
7878
{
7979
public StartsWithGenerator()
8080
{
81-
SupportedMethods = new[] { ReflectHelper.GetMethodDefinition<string>(x => x.StartsWith(null)) };
81+
SupportedMethods = new[] { ReflectionCache.StringMethods.StartsWith };
8282
}
8383

8484
public override bool AllowsNullableReturnType(MethodInfo method) => false;
@@ -97,7 +97,7 @@ public class EndsWithGenerator : BaseHqlGeneratorForMethod
9797
{
9898
public EndsWithGenerator()
9999
{
100-
SupportedMethods = new[] { ReflectHelper.GetMethodDefinition<string>(x => x.EndsWith(null)) };
100+
SupportedMethods = new[] { ReflectionCache.StringMethods.EndsWith };
101101
}
102102

103103
public override bool AllowsNullableReturnType(MethodInfo method) => false;
@@ -116,7 +116,7 @@ public class ContainsGenerator : BaseHqlGeneratorForMethod
116116
{
117117
public ContainsGenerator()
118118
{
119-
SupportedMethods = new[] { ReflectHelper.GetMethodDefinition<string>(x => x.Contains(null)) };
119+
SupportedMethods = new[] { ReflectionCache.StringMethods.Contains };
120120
}
121121

122122
public override bool AllowsNullableReturnType(MethodInfo method) => false;

src/NHibernate/Linq/NhRelinqQueryParser.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ static NhRelinqQueryParser()
2929
// NH-3247: must remove .Net compiler char to int conversion before
3030
// parameterization occurs.
3131
preTransformerRegistry.Register(new RemoveCharToIntConversion());
32+
preTransformerRegistry.Register(new LikeTransformer());
3233
PreProcessor = new TransformingExpressionTreeProcessor(preTransformerRegistry);
3334

3435
var transformerRegistry = ExpressionTransformerRegistry.CreateDefault();

src/NHibernate/Util/ReflectionCache.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,5 +222,13 @@ internal static class TypeMethods
222222
internal static readonly MethodInfo GetTypeFromHandle =
223223
ReflectHelper.FastGetMethod(System.Type.GetTypeFromHandle, default(RuntimeTypeHandle));
224224
}
225+
226+
internal static class StringMethods
227+
{
228+
public static readonly MethodInfo EndsWith = ReflectHelper.GetMethodDefinition<string>(x => x.EndsWith(null));
229+
public static readonly MethodInfo StartsWith = ReflectHelper.GetMethodDefinition<string>(x => x.StartsWith(null));
230+
public static readonly MethodInfo Contains = ReflectHelper.GetMethodDefinition<string>(x => x.Contains(null));
231+
public static readonly MethodInfo Concat = ReflectHelper.FastGetMethod(string.Concat, default(string), default(string));
232+
}
225233
}
226234
}

0 commit comments

Comments
 (0)