From c43b6c2f02c9bbeb05ed18ed6aaa46c589f36770 Mon Sep 17 00:00:00 2001 From: Jiri Cincura Date: Tue, 2 Apr 2024 10:29:38 +0200 Subject: [PATCH] List as inline collections implementation. --- .../Query/RelationalQueryRootProcessor.cs | 4 +- src/EFCore/Query/QueryRootProcessor.cs | 12 +- .../PrimitiveCollectionsQueryTestBase.cs | 63 +++++++++++ ...imitiveCollectionsQueryOldSqlServerTest.cs | 106 ++++++++++++++++++ .../PrimitiveCollectionsQuerySqlServerTest.cs | 102 +++++++++++++++++ .../PrimitiveCollectionsQuerySqliteTest.cs | 100 +++++++++++++++++ 6 files changed, 382 insertions(+), 5 deletions(-) diff --git a/src/EFCore.Relational/Query/RelationalQueryRootProcessor.cs b/src/EFCore.Relational/Query/RelationalQueryRootProcessor.cs index 7f2e5d4ab0b..a9449c4411b 100644 --- a/src/EFCore.Relational/Query/RelationalQueryRootProcessor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryRootProcessor.cs @@ -27,11 +27,11 @@ public RelationalQueryRootProcessor( } /// - /// Indicates that a can be converted to a ; + /// Indicates that a can be converted to a ; /// the latter will end up in for /// translation to a SQL . /// - protected override bool ShouldConvertToInlineQueryRoot(NewArrayExpression newArrayExpression) + protected override bool ShouldConvertToInlineQueryRoot(Expression expression) => true; /// diff --git a/src/EFCore/Query/QueryRootProcessor.cs b/src/EFCore/Query/QueryRootProcessor.cs index b8fb5427fcd..84167213994 100644 --- a/src/EFCore/Query/QueryRootProcessor.cs +++ b/src/EFCore/Query/QueryRootProcessor.cs @@ -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); + default: return Visit(expression); } } /// - /// Determines whether a should be converted to a . + /// Determines whether a should be converted to a . /// - /// The new array expression that's a candidate for conversion to a query root. - protected virtual bool ShouldConvertToInlineQueryRoot(NewArrayExpression newArrayExpression) + /// The expression that's a candidate for conversion to a query root. + protected virtual bool ShouldConvertToInlineQueryRoot(Expression expression) => false; /// diff --git a/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs index 384a8db4a43..1e56a23a06e 100644 --- a/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs @@ -138,6 +138,17 @@ await AssertQuery( ss => ss.Set().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) + { + var i = 11; + + await AssertQuery( + async, + ss => ss.Set().Where(c => new List() { 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) @@ -159,6 +170,13 @@ public virtual async Task Inline_collection_Min_with_two_values(bool async) async, ss => ss.Set().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().Where(c => new List() { 30, c.Int }.Min() == 30)); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual async Task Inline_collection_Max_with_two_values(bool async) @@ -166,6 +184,13 @@ public virtual async Task Inline_collection_Max_with_two_values(bool async) async, ss => ss.Set().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().Where(c => new List() { 30, c.Int }.Max() == 30)); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual async Task Inline_collection_Min_with_three_values(bool async) @@ -177,6 +202,17 @@ await AssertQuery( ss => ss.Set().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().Where(c => new List() { 30, c.Int, i }.Min() == 25)); + } + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual async Task Inline_collection_Max_with_three_values(bool async) @@ -188,6 +224,17 @@ await AssertQuery( ss => ss.Set().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().Where(c => new List() { 30, c.Int, i }.Max() == 35)); + } + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Parameter_collection_Count(bool async) @@ -483,6 +530,22 @@ public virtual Task Inline_collection_index_Column(bool async) ss => ss.Set().Where(c => new[] { 1, 2, 3 }[c.Int] == 1), ss => ss.Set().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().Where(c => new[] { 1, c.Int, 3 }[c.Int] == 1), + ss => ss.Set().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().Where(c => new List() { 1, c.Int, 3 }[c.Int] == 1), + ss => ss.Set().Where(c => (c.Int <= 2 ? new List() { 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] diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs index 80fdfacd6e1..eec352c4d7e 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs @@ -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]) @@ -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); @@ -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); @@ -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 ( @@ -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 ( @@ -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)); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs index 1c174b1ab8e..82446229a06 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs @@ -198,6 +198,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]) @@ -241,6 +255,19 @@ WHERE LEAST(30, [p].[Int]) = 30 """); } + [SqlServerCondition(SqlServerCondition.SupportsFunctions2022)] + 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 LEAST(30, [p].[Int]) = 30 +"""); + } + [SqlServerCondition(SqlServerCondition.SupportsFunctions2022)] public override async Task Inline_collection_Max_with_two_values(bool async) { @@ -254,6 +281,19 @@ WHERE GREATEST(30, [p].[Int]) = 30 """); } + [SqlServerCondition(SqlServerCondition.SupportsFunctions2022)] + 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 GREATEST(30, [p].[Int]) = 30 +"""); + } + [SqlServerCondition(SqlServerCondition.SupportsFunctions2022)] public override async Task Inline_collection_Min_with_three_values(bool async) { @@ -263,6 +303,21 @@ 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 LEAST(30, [p].[Int], @__i_0) = 25 +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsFunctions2022)] + 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 LEAST(30, [p].[Int], @__i_0) = 25 @@ -278,6 +333,21 @@ 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 GREATEST(30, [p].[Int], @__i_0) = 35 +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsFunctions2022)] + 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 GREATEST(30, [p].[Int], @__i_0) = 35 @@ -807,6 +877,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 +"""); + } + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] public override async Task Parameter_collection_index_Column_equal_Column(bool async) { diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs index 840fd16fed7..b341480199a 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs @@ -204,6 +204,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") @@ -246,6 +260,18 @@ WHERE min(30, "p"."Int") = 30 """); } + 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 min(30, "p"."Int") = 30 +"""); + } + public override async Task Inline_collection_Max_with_two_values(bool async) { await base.Inline_collection_Max_with_two_values(async); @@ -258,6 +284,18 @@ WHERE max(30, "p"."Int") = 30 """); } + 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 max(30, "p"."Int") = 30 +"""); + } + public override async Task Inline_collection_Min_with_three_values(bool async) { await base.Inline_collection_Min_with_three_values(async); @@ -266,6 +304,20 @@ 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 min(30, "p"."Int", @__i_0) = 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 min(30, "p"."Int", @__i_0) = 25 @@ -280,6 +332,20 @@ 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 max(30, "p"."Int", @__i_0) = 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 max(30, "p"."Int", @__i_0) = 35 @@ -793,6 +859,40 @@ ORDER BY "v"."_ord" """); } + public override async Task Inline_collection_value_index_Column(bool async) + { + // SQLite doesn't support correlated subqueries where the outer column is used as the LIMIT/OFFSET (see OFFSET "p"."Int" below) + await Assert.ThrowsAsync(() => 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 (SELECT 0 AS "_ord", CAST(1 AS INTEGER) AS "Value" UNION ALL VALUES (1, "p"."Int"), (2, 3)) AS "v" + ORDER BY "v"."_ord" + LIMIT 1 OFFSET "p"."Int") = 1 +"""); + } + + public override async Task Inline_collection_List_value_index_Column(bool async) + { + // SQLite doesn't support correlated subqueries where the outer column is used as the LIMIT/OFFSET (see OFFSET "p"."Int" below) + await Assert.ThrowsAsync(() => 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 (SELECT 0 AS "_ord", CAST(1 AS INTEGER) AS "Value" UNION ALL VALUES (1, "p"."Int"), (2, 3)) AS "v" + ORDER BY "v"."_ord" + LIMIT 1 OFFSET "p"."Int") = 1 +"""); + } + public override async Task Parameter_collection_index_Column_equal_Column(bool async) { await base.Parameter_collection_index_Column_equal_Column(async);