Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

List<T> as inline collections implementation. #33476

Merged
merged 1 commit into from
Apr 5, 2024
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
4 changes: 2 additions & 2 deletions src/EFCore.Relational/Query/RelationalQueryRootProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ public RelationalQueryRootProcessor(
}

/// <summary>
/// Indicates that a <see cref="NewArrayExpression" /> can be converted to a <see cref="InlineQueryRootExpression" />;
/// Indicates that a <see cref="Expression" /> can be converted to a <see cref="InlineQueryRootExpression" />;
/// the latter will end up in <see cref="RelationalQueryableMethodTranslatingExpressionVisitor.TranslateInlineQueryRoot" /> for
/// translation to a SQL <see cref="ValuesExpression" />.
/// </summary>
protected override bool ShouldConvertToInlineQueryRoot(NewArrayExpression newArrayExpression)
protected override bool ShouldConvertToInlineQueryRoot(Expression expression)
=> true;

/// <summary>
Expand Down
12 changes: 9 additions & 3 deletions src/EFCore/Query/QueryRootProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,16 +115,22 @@ when ShouldConvertToInlineQueryRoot(newArrayExpression):
&& ShouldConvertToParameterQueryRoot(parameterExpression):
return new ParameterQueryRootExpression(parameterExpression.Type.GetSequenceType(), parameterExpression);

case ListInitExpression listInitExpression
when listInitExpression.Type.TryGetElementType(typeof(IList<>)) is not null
&& listInitExpression.Initializers.All(x => x.Arguments.Count == 1)
&& ShouldConvertToInlineQueryRoot(listInitExpression):
return new InlineQueryRootExpression(listInitExpression.Initializers.Select(x => x.Arguments[0]).ToList(), elementClrType);
cincuranet marked this conversation as resolved.
Show resolved Hide resolved

default:
return Visit(expression);
}
}

/// <summary>
/// Determines whether a <see cref="NewArrayExpression" /> should be converted to a <see cref="InlineQueryRootExpression" />.
/// Determines whether a <see cref="Expression" /> should be converted to a <see cref="InlineQueryRootExpression" />.
/// </summary>
/// <param name="newArrayExpression">The new array expression that's a candidate for conversion to a query root.</param>
protected virtual bool ShouldConvertToInlineQueryRoot(NewArrayExpression newArrayExpression)
/// <param name="expression">The expression that's a candidate for conversion to a query root.</param>
protected virtual bool ShouldConvertToInlineQueryRoot(Expression expression)
=> false;

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,17 @@ await AssertQuery(
ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => new[] { 999, i, c.Id, c.Id + c.Int }.Contains(c.Int)));
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Inline_collection_List_Contains_with_mixed_value_types(bool async)
cincuranet marked this conversation as resolved.
Show resolved Hide resolved
{
var i = 11;

await AssertQuery(
async,
ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => new List<int>() { 999, i, c.Id, c.Id + c.Int }.Contains(c.Int)));
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Inline_collection_Contains_as_Any_with_predicate(bool async)
Expand All @@ -159,13 +170,27 @@ public virtual async Task Inline_collection_Min_with_two_values(bool async)
async,
ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => new[] { 30, c.Int }.Min() == 30));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Inline_collection_List_Min_with_two_values(bool async)
=> await AssertQuery(
async,
ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => new List<int>() { 30, c.Int }.Min() == 30));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Inline_collection_Max_with_two_values(bool async)
=> await AssertQuery(
async,
ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => new[] { 30, c.Int }.Max() == 30));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Inline_collection_List_Max_with_two_values(bool async)
=> await AssertQuery(
async,
ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => new List<int>() { 30, c.Int }.Max() == 30));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Inline_collection_Min_with_three_values(bool async)
Expand All @@ -177,6 +202,17 @@ await AssertQuery(
ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => new[] { 30, c.Int, i }.Min() == 25));
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Inline_collection_List_Min_with_three_values(bool async)
{
var i = 25;

await AssertQuery(
async,
ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => new List<int>() { 30, c.Int, i }.Min() == 25));
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Inline_collection_Max_with_three_values(bool async)
Expand All @@ -188,6 +224,17 @@ await AssertQuery(
ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => new[] { 30, c.Int, i }.Max() == 35));
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Inline_collection_List_Max_with_three_values(bool async)
{
var i = 35;

await AssertQuery(
async,
ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => new List<int>() { 30, c.Int, i }.Max() == 35));
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Parameter_collection_Count(bool async)
Expand Down Expand Up @@ -483,6 +530,22 @@ public virtual Task Inline_collection_index_Column(bool async)
ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => new[] { 1, 2, 3 }[c.Int] == 1),
ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => (c.Int <= 2 ? new[] { 1, 2, 3 }[c.Int] : -1) == 1));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Inline_collection_value_index_Column(bool async)
=> AssertQuery(
async,
ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => new[] { 1, c.Int, 3 }[c.Int] == 1),
ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => (c.Int <= 2 ? new[] { 1, c.Int, 3 }[c.Int] : -1) == 1));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Inline_collection_List_value_index_Column(bool async)
=> AssertQuery(
async,
ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => new List<int>() { 1, c.Int, 3 }[c.Int] == 1),
ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => (c.Int <= 2 ? new List<int>() { 1, c.Int, 3 }[c.Int] : -1) == 1));

// The JsonScalarExpression (ints[c.Int]) should get inferred from the column on the other side (c.Int), and that should propagate to
// ints
[ConditionalTheory]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,20 @@ public override async Task Inline_collection_Contains_with_mixed_value_types(boo
"""
@__i_0='11'

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].[String], [p].[Strings]
FROM [PrimitiveCollectionsEntity] AS [p]
WHERE [p].[Int] IN (999, @__i_0, [p].[Id], [p].[Id] + [p].[Int])
""");
}

public override async Task Inline_collection_List_Contains_with_mixed_value_types(bool async)
{
await base.Inline_collection_List_Contains_with_mixed_value_types(async);

AssertSql(
"""
@__i_0='11'

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].[String], [p].[Strings]
FROM [PrimitiveCollectionsEntity] AS [p]
WHERE [p].[Int] IN (999, @__i_0, [p].[Id], [p].[Id] + [p].[Int])
Expand Down Expand Up @@ -254,6 +268,20 @@ SELECT MIN([v].[Value])
""");
}

public override async Task Inline_collection_List_Min_with_two_values(bool async)
{
await base.Inline_collection_List_Min_with_two_values(async);

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].[String], [p].[Strings]
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 Inline_collection_Max_with_two_values(bool async)
{
await base.Inline_collection_Max_with_two_values(async);
Expand All @@ -268,6 +296,20 @@ SELECT MAX([v].[Value])
""");
}

public override async Task Inline_collection_List_Max_with_two_values(bool async)
{
await base.Inline_collection_List_Max_with_two_values(async);

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].[String], [p].[Strings]
FROM [PrimitiveCollectionsEntity] AS [p]
WHERE (
SELECT MAX([v].[Value])
FROM (VALUES (CAST(30 AS int)), ([p].[Int])) AS [v]([Value])) = 30
""");
}

public override async Task Inline_collection_Min_with_three_values(bool async)
{
await base.Inline_collection_Min_with_three_values(async);
Expand All @@ -276,6 +318,22 @@ public override async Task Inline_collection_Min_with_three_values(bool async)
"""
@__i_0='25'

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].[String], [p].[Strings]
FROM [PrimitiveCollectionsEntity] AS [p]
WHERE (
SELECT MIN([v].[Value])
FROM (VALUES (CAST(30 AS int)), ([p].[Int]), (@__i_0)) AS [v]([Value])) = 25
""");
}

public override async Task Inline_collection_List_Min_with_three_values(bool async)
{
await base.Inline_collection_List_Min_with_three_values(async);

AssertSql(
"""
@__i_0='25'

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].[String], [p].[Strings]
FROM [PrimitiveCollectionsEntity] AS [p]
WHERE (
Expand All @@ -292,6 +350,22 @@ public override async Task Inline_collection_Max_with_three_values(bool async)
"""
@__i_0='35'

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].[String], [p].[Strings]
FROM [PrimitiveCollectionsEntity] AS [p]
WHERE (
SELECT MAX([v].[Value])
FROM (VALUES (CAST(30 AS int)), ([p].[Int]), (@__i_0)) AS [v]([Value])) = 35
""");
}

public override async Task Inline_collection_List_Max_with_three_values(bool async)
{
await base.Inline_collection_List_Max_with_three_values(async);

AssertSql(
"""
@__i_0='35'

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].[String], [p].[Strings]
FROM [PrimitiveCollectionsEntity] AS [p]
WHERE (
Expand Down Expand Up @@ -564,6 +638,38 @@ ORDER BY [v].[_ord]
""");
}

public override async Task Inline_collection_value_index_Column(bool async)
{
await base.Inline_collection_value_index_Column(async);

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].[String], [p].[Strings]
FROM [PrimitiveCollectionsEntity] AS [p]
WHERE (
SELECT [v].[Value]
FROM (VALUES (0, CAST(1 AS int)), (1, [p].[Int]), (2, 3)) AS [v]([_ord], [Value])
ORDER BY [v].[_ord]
OFFSET [p].[Int] ROWS FETCH NEXT 1 ROWS ONLY) = 1
""");
}

public override async Task Inline_collection_List_value_index_Column(bool async)
{
await base.Inline_collection_List_value_index_Column(async);

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].[String], [p].[Strings]
FROM [PrimitiveCollectionsEntity] AS [p]
WHERE (
SELECT [v].[Value]
FROM (VALUES (0, CAST(1 AS int)), (1, [p].[Int]), (2, 3)) AS [v]([_ord], [Value])
ORDER BY [v].[_ord]
OFFSET [p].[Int] ROWS FETCH NEXT 1 ROWS ONLY) = 1
""");
}

public override Task Parameter_collection_index_Column_equal_Column(bool async)
=> AssertCompatibilityLevelTooLow(() => base.Parameter_collection_index_Column_equal_Column(async));

Expand Down
Loading
Loading