Skip to content

Commit

Permalink
Update SearchCondition visitor with improvements from SQL Server vers…
Browse files Browse the repository at this point in the history
…ion (#255)

* Update SearchCondition visitor with improvements from SQL Server version
  • Loading branch information
ChrisJollyAU authored Aug 25, 2024
1 parent 8a5c4d8 commit bf6e8a1
Show file tree
Hide file tree
Showing 13 changed files with 954 additions and 851 deletions.
1,070 changes: 511 additions & 559 deletions src/EFCore.Jet/Query/Internal/SearchConditionConvertingExpressionVisitor.cs

Large diffs are not rendered by default.

130 changes: 130 additions & 0 deletions src/Shared/ExpressionVisitorExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
using Microsoft.EntityFrameworkCore.Diagnostics;
using System.Collections.Generic;
using System.Diagnostics;
// ReSharper disable once CheckNamespace
using System.Runtime.CompilerServices;

namespace System.Linq.Expressions;

#nullable enable

[DebuggerStepThrough]
internal static class ExpressionVisitorExtensions
{
/// <summary>
/// Dispatches the list of expressions to one of the more specialized visit methods in this class.
/// </summary>
/// <param name="visitor">The expression visitor.</param>
/// <param name="nodes">The expressions to visit.</param>
/// <returns>
/// The modified expression list, if any of the elements were modified; otherwise, returns the original expression list.
/// </returns>
public static IReadOnlyList<Expression> Visit(this ExpressionVisitor visitor, IReadOnlyList<Expression> nodes)
{
Expression[]? newNodes = null;
for (int i = 0, n = nodes.Count; i < n; i++)
{
var node = visitor.Visit(nodes[i]);

if (newNodes is not null)
{
newNodes[i] = node;
}
else if (!ReferenceEquals(node, nodes[i]))
{
newNodes = new Expression[n];
for (var j = 0; j < i; j++)
{
newNodes[j] = nodes[j];
}

newNodes[i] = node;
}
}

return newNodes ?? nodes;
}

/// <summary>
/// Visits an expression, casting the result back to the original expression type.
/// </summary>
/// <typeparam name="T">The type of the expression.</typeparam>
/// <param name="visitor">The expression visitor.</param>
/// <param name="nodes">The expression to visit.</param>
/// <param name="callerName">The name of the calling method; used to report to report a better error message.</param>
/// <returns>
/// The modified expression, if it or any subexpression was modified; otherwise, returns the original expression.
/// </returns>
/// <exception cref="InvalidOperationException">The visit method for this node returned a different type.</exception>
public static IReadOnlyList<T> VisitAndConvert<T>(
this ExpressionVisitor visitor,
IReadOnlyList<T> nodes,
[CallerMemberName] string? callerName = null)
where T : Expression
{
T[]? newNodes = null;
for (int i = 0, n = nodes.Count; i < n; i++)
{
if (visitor.Visit(nodes[i]) is not T node)
{
throw new InvalidOperationException(CoreStrings.MustRewriteToSameNode(callerName, typeof(T).Name));
}

if (newNodes is not null)
{
newNodes[i] = node;
}
else if (!ReferenceEquals(node, nodes[i]))
{
newNodes = new T[n];
for (var j = 0; j < i; j++)
{
newNodes[j] = nodes[j];
}

newNodes[i] = node;
}
}

return newNodes ?? nodes;
}

/// <summary>
/// Visits all nodes in the collection using a specified element visitor.
/// </summary>
/// <typeparam name="T">The type of the nodes.</typeparam>
/// <param name="visitor">The expression visitor.</param>
/// <param name="nodes">The nodes to visit.</param>
/// <param name="elementVisitor">
/// A delegate that visits a single element,
/// optionally replacing it with a new element.
/// </param>
/// <returns>
/// The modified node list, if any of the elements were modified;
/// otherwise, returns the original node list.
/// </returns>
public static IReadOnlyList<T> Visit<T>(this ExpressionVisitor visitor, IReadOnlyList<T> nodes, Func<T, T> elementVisitor)
{
T[]? newNodes = null;
for (int i = 0, n = nodes.Count; i < n; i++)
{
var node = elementVisitor(nodes[i]);
if (newNodes is not null)
{
newNodes[i] = node;
}
else if (!ReferenceEquals(node, nodes[i]))
{
newNodes = new T[n];
for (var j = 0; j < i; j++)
{
newNodes[j] = nodes[j];
}

newNodes[i] = node;
}
}

return newNodes ?? nodes;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12755,6 +12755,8 @@ EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Select_enum
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Select_enum_has_flag(isAsync: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Select_inverted_boolean(isAsync: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Select_inverted_boolean(isAsync: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Select_inverted_nullable_boolean(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Select_inverted_nullable_boolean(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Select_length_of_string_property(isAsync: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Select_length_of_string_property(isAsync: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Select_multiple_conditions(isAsync: False)
Expand Down Expand Up @@ -19827,6 +19829,8 @@ EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Select_e
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Select_enum_has_flag(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Select_inverted_boolean(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Select_inverted_boolean(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Select_inverted_nullable_boolean(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Select_inverted_nullable_boolean(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Select_length_of_string_property(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Select_length_of_string_property(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Select_multiple_conditions(async: False)
Expand Down Expand Up @@ -21658,6 +21662,8 @@ EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Select_e
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Select_enum_has_flag(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Select_inverted_boolean(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Select_inverted_boolean(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Select_inverted_nullable_boolean(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Select_inverted_nullable_boolean(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Select_length_of_string_property(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Select_length_of_string_property(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Select_multiple_conditions(async: False)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public override async Task Expression_tree_constructed_via_interface_works()
"""
SELECT `r`.`Id`, `r`.`IsRemoved`, `r`.`Removed`, `r`.`RemovedByUser`, `r`.`OwnedEntity_Exists`, `r`.`OwnedEntity_OwnedValue`
FROM `RemovableEntities` AS `r`
WHERE `r`.`IsRemoved` <> TRUE
WHERE `r`.`IsRemoved` = FALSE
""",
//
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1247,7 +1247,7 @@ public override async Task Conditional_expression_with_conditions_does_not_colla

AssertSql(
"""
SELECT IIF(`c0`.`Id` IS NOT NULL, IIF(`c0`.`Processed` <> TRUE, TRUE, FALSE), NULL) AS `Processing`
SELECT IIF(`c0`.`Id` IS NOT NULL, `c0`.`Processed` BXOR TRUE, NULL) AS `Processing`
FROM `Carts` AS `c`
LEFT JOIN `Configuration` AS `c0` ON `c`.`ConfigurationId` = `c0`.`Id`
""");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ await AssertQueryScalar(
"""
SELECT CBOOL(ISDATE(`o`.`CustomerID`))
FROM `Orders` AS `o`
WHERE CBOOL(ISDATE(`o`.`CustomerID`)) <> TRUE
WHERE CBOOL(ISDATE(`o`.`CustomerID`)) = FALSE
""");
}

Expand Down
Loading

0 comments on commit bf6e8a1

Please sign in to comment.