Skip to content

Commit bf6e8a1

Browse files
authored
Update SearchCondition visitor with improvements from SQL Server version (#255)
* Update SearchCondition visitor with improvements from SQL Server version
1 parent 8a5c4d8 commit bf6e8a1

13 files changed

+954
-851
lines changed

src/EFCore.Jet/Query/Internal/SearchConditionConvertingExpressionVisitor.cs

Lines changed: 511 additions & 559 deletions
Large diffs are not rendered by default.
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
using Microsoft.EntityFrameworkCore.Diagnostics;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
// ReSharper disable once CheckNamespace
5+
using System.Runtime.CompilerServices;
6+
7+
namespace System.Linq.Expressions;
8+
9+
#nullable enable
10+
11+
[DebuggerStepThrough]
12+
internal static class ExpressionVisitorExtensions
13+
{
14+
/// <summary>
15+
/// Dispatches the list of expressions to one of the more specialized visit methods in this class.
16+
/// </summary>
17+
/// <param name="visitor">The expression visitor.</param>
18+
/// <param name="nodes">The expressions to visit.</param>
19+
/// <returns>
20+
/// The modified expression list, if any of the elements were modified; otherwise, returns the original expression list.
21+
/// </returns>
22+
public static IReadOnlyList<Expression> Visit(this ExpressionVisitor visitor, IReadOnlyList<Expression> nodes)
23+
{
24+
Expression[]? newNodes = null;
25+
for (int i = 0, n = nodes.Count; i < n; i++)
26+
{
27+
var node = visitor.Visit(nodes[i]);
28+
29+
if (newNodes is not null)
30+
{
31+
newNodes[i] = node;
32+
}
33+
else if (!ReferenceEquals(node, nodes[i]))
34+
{
35+
newNodes = new Expression[n];
36+
for (var j = 0; j < i; j++)
37+
{
38+
newNodes[j] = nodes[j];
39+
}
40+
41+
newNodes[i] = node;
42+
}
43+
}
44+
45+
return newNodes ?? nodes;
46+
}
47+
48+
/// <summary>
49+
/// Visits an expression, casting the result back to the original expression type.
50+
/// </summary>
51+
/// <typeparam name="T">The type of the expression.</typeparam>
52+
/// <param name="visitor">The expression visitor.</param>
53+
/// <param name="nodes">The expression to visit.</param>
54+
/// <param name="callerName">The name of the calling method; used to report to report a better error message.</param>
55+
/// <returns>
56+
/// The modified expression, if it or any subexpression was modified; otherwise, returns the original expression.
57+
/// </returns>
58+
/// <exception cref="InvalidOperationException">The visit method for this node returned a different type.</exception>
59+
public static IReadOnlyList<T> VisitAndConvert<T>(
60+
this ExpressionVisitor visitor,
61+
IReadOnlyList<T> nodes,
62+
[CallerMemberName] string? callerName = null)
63+
where T : Expression
64+
{
65+
T[]? newNodes = null;
66+
for (int i = 0, n = nodes.Count; i < n; i++)
67+
{
68+
if (visitor.Visit(nodes[i]) is not T node)
69+
{
70+
throw new InvalidOperationException(CoreStrings.MustRewriteToSameNode(callerName, typeof(T).Name));
71+
}
72+
73+
if (newNodes is not null)
74+
{
75+
newNodes[i] = node;
76+
}
77+
else if (!ReferenceEquals(node, nodes[i]))
78+
{
79+
newNodes = new T[n];
80+
for (var j = 0; j < i; j++)
81+
{
82+
newNodes[j] = nodes[j];
83+
}
84+
85+
newNodes[i] = node;
86+
}
87+
}
88+
89+
return newNodes ?? nodes;
90+
}
91+
92+
/// <summary>
93+
/// Visits all nodes in the collection using a specified element visitor.
94+
/// </summary>
95+
/// <typeparam name="T">The type of the nodes.</typeparam>
96+
/// <param name="visitor">The expression visitor.</param>
97+
/// <param name="nodes">The nodes to visit.</param>
98+
/// <param name="elementVisitor">
99+
/// A delegate that visits a single element,
100+
/// optionally replacing it with a new element.
101+
/// </param>
102+
/// <returns>
103+
/// The modified node list, if any of the elements were modified;
104+
/// otherwise, returns the original node list.
105+
/// </returns>
106+
public static IReadOnlyList<T> Visit<T>(this ExpressionVisitor visitor, IReadOnlyList<T> nodes, Func<T, T> elementVisitor)
107+
{
108+
T[]? newNodes = null;
109+
for (int i = 0, n = nodes.Count; i < n; i++)
110+
{
111+
var node = elementVisitor(nodes[i]);
112+
if (newNodes is not null)
113+
{
114+
newNodes[i] = node;
115+
}
116+
else if (!ReferenceEquals(node, nodes[i]))
117+
{
118+
newNodes = new T[n];
119+
for (var j = 0; j < i; j++)
120+
{
121+
newNodes[j] = nodes[j];
122+
}
123+
124+
newNodes[i] = node;
125+
}
126+
}
127+
128+
return newNodes ?? nodes;
129+
}
130+
}

test/EFCore.Jet.FunctionalTests/GreenTests/ace_2010_oledb_x86.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12755,6 +12755,8 @@ EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Select_enum
1275512755
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Select_enum_has_flag(isAsync: True)
1275612756
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Select_inverted_boolean(isAsync: False)
1275712757
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Select_inverted_boolean(isAsync: True)
12758+
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Select_inverted_nullable_boolean(async: False)
12759+
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Select_inverted_nullable_boolean(async: True)
1275812760
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Select_length_of_string_property(isAsync: False)
1275912761
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Select_length_of_string_property(isAsync: True)
1276012762
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Select_multiple_conditions(isAsync: False)
@@ -19827,6 +19829,8 @@ EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Select_e
1982719829
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Select_enum_has_flag(async: True)
1982819830
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Select_inverted_boolean(async: False)
1982919831
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Select_inverted_boolean(async: True)
19832+
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Select_inverted_nullable_boolean(async: False)
19833+
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Select_inverted_nullable_boolean(async: True)
1983019834
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Select_length_of_string_property(async: False)
1983119835
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Select_length_of_string_property(async: True)
1983219836
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Select_multiple_conditions(async: False)
@@ -21658,6 +21662,8 @@ EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Select_e
2165821662
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Select_enum_has_flag(async: True)
2165921663
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Select_inverted_boolean(async: False)
2166021664
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Select_inverted_boolean(async: True)
21665+
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Select_inverted_nullable_boolean(async: False)
21666+
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Select_inverted_nullable_boolean(async: True)
2166121667
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Select_length_of_string_property(async: False)
2166221668
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Select_length_of_string_property(async: True)
2166321669
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Select_multiple_conditions(async: False)

test/EFCore.Jet.FunctionalTests/Query/AdHocAdvancedMappingsQueryJetTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public override async Task Expression_tree_constructed_via_interface_works()
7373
"""
7474
SELECT `r`.`Id`, `r`.`IsRemoved`, `r`.`Removed`, `r`.`RemovedByUser`, `r`.`OwnedEntity_Exists`, `r`.`OwnedEntity_OwnedValue`
7575
FROM `RemovableEntities` AS `r`
76-
WHERE `r`.`IsRemoved` <> TRUE
76+
WHERE `r`.`IsRemoved` = FALSE
7777
""",
7878
//
7979
"""

test/EFCore.Jet.FunctionalTests/Query/AdHocMiscellaneousQueryJetTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1247,7 +1247,7 @@ public override async Task Conditional_expression_with_conditions_does_not_colla
12471247

12481248
AssertSql(
12491249
"""
1250-
SELECT IIF(`c0`.`Id` IS NOT NULL, IIF(`c0`.`Processed` <> TRUE, TRUE, FALSE), NULL) AS `Processing`
1250+
SELECT IIF(`c0`.`Id` IS NOT NULL, `c0`.`Processed` BXOR TRUE, NULL) AS `Processing`
12511251
FROM `Carts` AS `c`
12521252
LEFT JOIN `Configuration` AS `c0` ON `c`.`ConfigurationId` = `c0`.`Id`
12531253
""");

test/EFCore.Jet.FunctionalTests/Query/DbFunctionsJetTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ await AssertQueryScalar(
436436
"""
437437
SELECT CBOOL(ISDATE(`o`.`CustomerID`))
438438
FROM `Orders` AS `o`
439-
WHERE CBOOL(ISDATE(`o`.`CustomerID`)) <> TRUE
439+
WHERE CBOOL(ISDATE(`o`.`CustomerID`)) = FALSE
440440
""");
441441
}
442442

0 commit comments

Comments
 (0)