Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1003,6 +1003,37 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCall)
Call(
EnumerableMethods.SequenceEqual.MakeGenericMethod(methodCall.Method.GetGenericArguments()[0]),
unwrappedSpanArg, unwrappedOtherArg));

// .NET 11 added Min/Max Span overloads; rewrite to Enumerable.Min/Max.
case "Min"
when methodCall.Arguments is [var spanArg]
&& TryUnwrapSpanImplicitCast(spanArg, out var unwrappedSpanArg):
{
var elementType = methodCall.Method.ReturnType;
var enumerableMin = EnumerableMethods.GetMinWithoutSelector(elementType);

if (enumerableMin.IsGenericMethodDefinition)
{
enumerableMin = enumerableMin.MakeGenericMethod(elementType);
}

return Visit(Call(enumerableMin, unwrappedSpanArg));
}

case "Max"
when methodCall.Arguments is [var spanArg]
&& TryUnwrapSpanImplicitCast(spanArg, out var unwrappedSpanArg):
{
var elementType = methodCall.Method.ReturnType;
var enumerableMax = EnumerableMethods.GetMaxWithoutSelector(elementType);

if (enumerableMax.IsGenericMethodDefinition)
{
enumerableMax = enumerableMax.MakeGenericMethod(elementType);
}

return Visit(Call(enumerableMax, unwrappedSpanArg));
}
Comment thread
AndriySvyryd marked this conversation as resolved.
}

static bool TryUnwrapSpanImplicitCast(Expression expression, [NotNullWhen(true)] out Expression? result)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2076,6 +2076,36 @@ FROM root c
""");
}

#if NET11_0_OR_GREATER
public override async Task Min_on_MemoryExtensions()
{
await base.Min_on_MemoryExtensions();

AssertSql(
"""
SELECT VALUE c
FROM root c
WHERE ((
SELECT VALUE MIN(a)
FROM a IN (SELECT VALUE [30, c["Int"]])) = 30)
""");
}

public override async Task Max_on_MemoryExtensions()
{
await base.Max_on_MemoryExtensions();

AssertSql(
"""
SELECT VALUE c
FROM root c
WHERE ((
SELECT VALUE MAX(a)
FROM a IN (SELECT VALUE [30, c["Int"]])) = 30)
""");
}
#endif

[ConditionalFact]
public virtual void Check_all_tests_overridden()
=> TestHelpers.AssertAllMethodsOverridden(GetType());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -955,6 +955,22 @@ public virtual Task Contains_with_MemoryExtensions_with_null_comparer()
=> AssertQuery(
ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => MemoryExtensions.Contains(new[] { 10, 999 }, c.Int, comparer: null)));

#if NET11_0_OR_GREATER
// .NET 11 first-class spans caused MemoryExtensions.Min to get resolved instead of Enumerable.Min.
// The following tests that the various overloads are all supported.
[ConditionalFact]
public virtual Task Min_on_MemoryExtensions()
=> AssertQuery(
ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => MemoryExtensions.Min(new[] { 30, c.Int }) == 30));

// .NET 11 first-class spans caused MemoryExtensions.Max to get resolved instead of Enumerable.Max.
// The following tests that the various overloads are all supported.
[ConditionalFact]
public virtual Task Max_on_MemoryExtensions()
=> AssertQuery(
ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => MemoryExtensions.Max(new[] { 30, c.Int }) == 30));
#endif

[ConditionalFact]
public virtual Task Column_collection_Count_method()
=> AssertQuery(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,36 @@ WHERE [p].[Int] IN (10, 999)
""");
}

#if NET11_0_OR_GREATER
public override async Task Min_on_MemoryExtensions()
{
await base.Min_on_MemoryExtensions();

AssertSql(
"""
SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId]
FROM [PrimitiveCollectionsEntity] AS [p]
WHERE (
SELECT MIN([v].[Value])
FROM (VALUES (CAST(30 AS int)), ([p].[Int])) AS [v]([Value])) = 30
""");
}

public override async Task Max_on_MemoryExtensions()
{
await base.Max_on_MemoryExtensions();

AssertSql(
"""
SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId]
FROM [PrimitiveCollectionsEntity] AS [p]
WHERE (
SELECT MAX([v].[Value])
FROM (VALUES (CAST(30 AS int)), ([p].[Int])) AS [v]([Value])) = 30
""");
}
#endif

public override Task Column_collection_Count_method()
=> AssertCompatibilityLevelTooLow(() => base.Column_collection_Count_method());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,32 @@ WHERE [p].[Int] IN (10, 999)
""");
}

#if NET11_0_OR_GREATER
public override async Task Min_on_MemoryExtensions()
{
await base.Min_on_MemoryExtensions();

AssertSql(
"""
SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId]
FROM [PrimitiveCollectionsEntity] AS [p]
WHERE LEAST(30, [p].[Int]) = 30
""");
}

public override async Task Max_on_MemoryExtensions()
{
await base.Max_on_MemoryExtensions();

AssertSql(
"""
SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId]
FROM [PrimitiveCollectionsEntity] AS [p]
WHERE GREATEST(30, [p].[Int]) = 30
""");
}
#endif

public override async Task Column_collection_Count_method()
{
await base.Column_collection_Count_method();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1268,6 +1268,32 @@ WHERE [p].[Int] IN (10, 999)
""");
}

#if NET11_0_OR_GREATER
public override async Task Min_on_MemoryExtensions()
{
await base.Min_on_MemoryExtensions();

AssertSql(
"""
SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId]
FROM [PrimitiveCollectionsEntity] AS [p]
WHERE LEAST(30, [p].[Int]) = 30
""");
}

public override async Task Max_on_MemoryExtensions()
{
await base.Max_on_MemoryExtensions();

AssertSql(
"""
SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId]
FROM [PrimitiveCollectionsEntity] AS [p]
WHERE GREATEST(30, [p].[Int]) = 30
""");
}
#endif

public override async Task Column_collection_Count_method()
{
await base.Column_collection_Count_method();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1118,6 +1118,36 @@ WHERE [p].[Int] IN (10, 999)
""");
}

#if NET11_0_OR_GREATER
public override async Task Min_on_MemoryExtensions()
{
await base.Min_on_MemoryExtensions();

AssertSql(
"""
SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId]
FROM [PrimitiveCollectionsEntity] AS [p]
WHERE (
SELECT MIN([v].[Value])
FROM (VALUES (CAST(30 AS int)), ([p].[Int])) AS [v]([Value])) = 30
""");
}

public override async Task Max_on_MemoryExtensions()
{
await base.Max_on_MemoryExtensions();

AssertSql(
"""
SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId]
FROM [PrimitiveCollectionsEntity] AS [p]
WHERE (
SELECT MAX([v].[Value])
FROM (VALUES (CAST(30 AS int)), ([p].[Int])) AS [v]([Value])) = 30
""");
}
#endif

public override async Task Column_collection_Count_method()
{
await base.Column_collection_Count_method();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1048,6 +1048,32 @@ public override async Task Contains_with_MemoryExtensions_with_null_comparer()
""");
}

#if NET11_0_OR_GREATER
public override async Task Min_on_MemoryExtensions()
{
await base.Min_on_MemoryExtensions();

AssertSql(
"""
SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId"
FROM "PrimitiveCollectionsEntity" AS "p"
WHERE min(30, "p"."Int") = 30
""");
}

public override async Task Max_on_MemoryExtensions()
{
await base.Max_on_MemoryExtensions();

AssertSql(
"""
SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId"
FROM "PrimitiveCollectionsEntity" AS "p"
WHERE max(30, "p"."Int") = 30
""");
}
#endif

public override async Task Column_collection_Count_method()
{
await base.Column_collection_Count_method();
Expand Down
Loading