From 147fb6799f1470f61093b49b8c62fa42a04dab85 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Tue, 13 Sep 2022 20:53:51 +0200 Subject: [PATCH] Sync EF Core to 7.0.0-rc.2.22462.15 --- Directory.Packages.props | 6 +- NuGet.config | 9 - global.json | 2 +- .../Internal/PostgresDeleteExpression.cs | 15 +- ...NpgsqlDeleteConvertingExpressionVisitor.cs | 2 +- .../Query/Internal/NpgsqlQuerySqlGenerator.cs | 16 + .../NpgsqlModificationCommandBatch.cs | 8 +- ...FiltersInheritanceBulkUpdatesNpgsqlTest.cs | 93 +++- .../InheritanceBulkUpdatesNpgsqlTest.cs | 102 ++++- .../NonSharedModelBulkUpdatesNpgsqlTest.cs | 27 ++ .../NorthwindBulkUpdatesNpgsqlTest.cs | 418 ++++++++++++------ ...FiltersInheritanceBulkUpdatesNpgsqlTest.cs | 64 ++- .../TPCInheritanceBulkUpdatesNpgsqlTest.cs | 64 ++- ...FiltersInheritanceBulkUpdatesNpgsqlTest.cs | 82 ++-- .../TPTInheritanceBulkUpdatesNpgsqlTest.cs | 58 ++- .../ManyToManyTrackingNpgsqlTest.cs | 5 +- .../Query/NorthwindGroupByQueryNpgsqlTest.cs | 172 ++++++- .../Query/ToSqlQueryNpgsqlTest.cs | 23 + .../TableSplittingNpgsqlTest.cs | 14 +- .../UpdatesNpgsqlFixture.cs | 9 - .../UpdatesNpgsqlTest.cs | 23 +- 21 files changed, 931 insertions(+), 281 deletions(-) create mode 100644 test/EFCore.PG.FunctionalTests/Query/ToSqlQueryNpgsqlTest.cs delete mode 100644 test/EFCore.PG.FunctionalTests/UpdatesNpgsqlFixture.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index ca95d73c..95002e20 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,7 +1,7 @@ - 7.0.0-rc.1.22426.7 - 7.0.0-rc.1.22426.10 + 7.0.0-rc.2.22462.15 + 7.0.0-rc.2.22462.6 7.0.0-preview.7 @@ -22,7 +22,7 @@ - + diff --git a/NuGet.config b/NuGet.config index 73c499fc..50bf9174 100644 --- a/NuGet.config +++ b/NuGet.config @@ -21,15 +21,6 @@ - - - - - - - - diff --git a/global.json b/global.json index 98802909..727f9b6e 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.100-preview.7.22377.5", + "version": "7.0.100-rc.1.22425.9", "rollForward": "latestMajor", "allowPrerelease": "true" } diff --git a/src/EFCore.PG/Query/Expressions/Internal/PostgresDeleteExpression.cs b/src/EFCore.PG/Query/Expressions/Internal/PostgresDeleteExpression.cs index 59ab8890..62e5090b 100644 --- a/src/EFCore.PG/Query/Expressions/Internal/PostgresDeleteExpression.cs +++ b/src/EFCore.PG/Query/Expressions/Internal/PostgresDeleteExpression.cs @@ -20,11 +20,20 @@ public sealed class PostgresDeleteExpression : Expression, IPrintableExpression /// public SqlExpression? Predicate { get; } + /// + /// The list of tags applied to this . + /// + public ISet Tags { get; } + /// /// Creates a new instance of the class. /// - public PostgresDeleteExpression(TableExpression table, IReadOnlyList fromItems, SqlExpression? predicate) - => (Table, FromItems, Predicate) = (table, fromItems, predicate); + public PostgresDeleteExpression( + TableExpression table, + IReadOnlyList fromItems, + SqlExpression? predicate, + ISet tags) + => (Table, FromItems, Predicate, Tags) = (table, fromItems, predicate, tags); /// public override Type Type @@ -48,7 +57,7 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) public PostgresDeleteExpression Update(SqlExpression? predicate) => predicate == Predicate ? this - : new PostgresDeleteExpression(Table, FromItems, predicate); + : new PostgresDeleteExpression(Table, FromItems, predicate, Tags); /// public void Print(ExpressionPrinter expressionPrinter) diff --git a/src/EFCore.PG/Query/Internal/NpgsqlDeleteConvertingExpressionVisitor.cs b/src/EFCore.PG/Query/Internal/NpgsqlDeleteConvertingExpressionVisitor.cs index da26d193..f387978f 100644 --- a/src/EFCore.PG/Query/Internal/NpgsqlDeleteConvertingExpressionVisitor.cs +++ b/src/EFCore.PG/Query/Internal/NpgsqlDeleteConvertingExpressionVisitor.cs @@ -93,6 +93,6 @@ protected virtual Expression VisitDelete(DeleteExpression deleteExpression) ExpressionType.AndAlso, joinPredicates, selectExpression.Predicate, typeof(bool), joinPredicates.TypeMapping) }; - return new PostgresDeleteExpression(deleteExpression.Table, fromItems, predicate); + return new PostgresDeleteExpression(deleteExpression.Table, fromItems, predicate, deleteExpression.Tags); } } diff --git a/src/EFCore.PG/Query/Internal/NpgsqlQuerySqlGenerator.cs b/src/EFCore.PG/Query/Internal/NpgsqlQuerySqlGenerator.cs index 77caf4e1..a5836f25 100644 --- a/src/EFCore.PG/Query/Internal/NpgsqlQuerySqlGenerator.cs +++ b/src/EFCore.PG/Query/Internal/NpgsqlQuerySqlGenerator.cs @@ -61,6 +61,22 @@ protected override Expression VisitExtension(Expression extensionExpression) _ => base.VisitExtension(extensionExpression) }; + /// + protected override void GenerateRootCommand(Expression queryExpression) + { + switch (queryExpression) + { + case PostgresDeleteExpression postgresDeleteExpression: + GenerateTagsHeaderComment(postgresDeleteExpression.Tags); + VisitPostgresDelete(postgresDeleteExpression); + break; + + default: + base.GenerateRootCommand(queryExpression); + break; + } + } + /// protected override void GenerateLimitOffset(SelectExpression selectExpression) { diff --git a/src/EFCore.PG/Update/Internal/NpgsqlModificationCommandBatch.cs b/src/EFCore.PG/Update/Internal/NpgsqlModificationCommandBatch.cs index 7ba8d1f8..f740df25 100644 --- a/src/EFCore.PG/Update/Internal/NpgsqlModificationCommandBatch.cs +++ b/src/EFCore.PG/Update/Internal/NpgsqlModificationCommandBatch.cs @@ -55,7 +55,7 @@ protected override void Consume(RelationalDataReader reader) int nextPropagating; for (nextPropagating = commandIndex; nextPropagating < ModificationCommands.Count && - !ModificationCommands[nextPropagating].RequiresResultPropagation; + !ResultSetMappings[nextPropagating].HasFlag(ResultSetMapping.HasResultRow); nextPropagating++) { } @@ -87,8 +87,6 @@ protected override void Consume(RelationalDataReader reader) ThrowAggregateUpdateConcurrencyException(reader, commandIndex, 1, 0); } - Check.DebugAssert(modificationCommand.RequiresResultPropagation, "RequiresResultPropagation is false"); - modificationCommand.PropagateResults(reader); npgsqlReader.NextResult(); @@ -129,7 +127,7 @@ protected override async Task ConsumeAsync(RelationalDataReader reader, Cancella int nextPropagating; for (nextPropagating = commandIndex; nextPropagating < ModificationCommands.Count && - !ModificationCommands[nextPropagating].RequiresResultPropagation; + !ResultSetMappings[nextPropagating].HasFlag(ResultSetMapping.HasResultRow); nextPropagating++) { } @@ -163,8 +161,6 @@ await ThrowAggregateUpdateConcurrencyExceptionAsync(reader, commandIndex, 1, 0, .ConfigureAwait(false); } - Check.DebugAssert(modificationCommand.RequiresResultPropagation, "RequiresResultPropagation is false"); - modificationCommand.PropagateResults(reader); await npgsqlReader.NextResultAsync(cancellationToken).ConfigureAwait(false); diff --git a/test/EFCore.PG.FunctionalTests/BulkUpdates/FiltersInheritanceBulkUpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BulkUpdates/FiltersInheritanceBulkUpdatesNpgsqlTest.cs index 60e059a0..b09e1740 100644 --- a/test/EFCore.PG.FunctionalTests/BulkUpdates/FiltersInheritanceBulkUpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BulkUpdates/FiltersInheritanceBulkUpdatesNpgsqlTest.cs @@ -4,10 +4,13 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.BulkUpdates; public class FiltersInheritanceBulkUpdatesNpgsqlTest : FiltersInheritanceBulkUpdatesTestBase { - public FiltersInheritanceBulkUpdatesNpgsqlTest(FiltersInheritanceBulkUpdatesNpgsqlFixture fixture) + public FiltersInheritanceBulkUpdatesNpgsqlTest( + FiltersInheritanceBulkUpdatesNpgsqlFixture fixture, + ITestOutputHelper testOutputHelper) : base(fixture) { ClearLog(); + // Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } public override async Task Delete_where_hierarchy(bool async) @@ -60,6 +63,40 @@ public override async Task Delete_where_using_hierarchy_derived(bool async) """); } + public override async Task Delete_GroupBy_Where_Select_First(bool async) + { + await base.Delete_GroupBy_Where_Select_First(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_2(bool async) + { + await base.Delete_GroupBy_Where_Select_First_2(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_3(bool async) + { + await base.Delete_GroupBy_Where_Select_First_3(async); + + AssertSql( +""" +DELETE FROM "Animals" AS a +WHERE a."CountryId" = 1 AND EXISTS ( + SELECT 1 + FROM "Animals" AS a0 + WHERE a0."CountryId" = 1 + GROUP BY a0."CountryId" + HAVING count(*)::int < 3 AND ( + SELECT a1."Id" + FROM "Animals" AS a1 + WHERE a1."CountryId" = 1 AND a0."CountryId" = a1."CountryId" + LIMIT 1) = a."Id") +"""); + } + public override async Task Delete_where_keyless_entity_mapped_to_sql_query(bool async) { await base.Delete_where_keyless_entity_mapped_to_sql_query(async); @@ -71,7 +108,23 @@ public override async Task Delete_where_hierarchy_subquery(bool async) { await base.Delete_where_hierarchy_subquery(async); - AssertSql(); + AssertSql( +""" +@__p_1='3' +@__p_0='0' + +DELETE FROM "Animals" AS a +WHERE EXISTS ( + SELECT 1 + FROM ( + SELECT a0."Id", a0."CountryId", a0."Discriminator", a0."Name", a0."Species", a0."EagleId", a0."IsFlightless", a0."Group", a0."FoundOn" + FROM "Animals" AS a0 + WHERE a0."CountryId" = 1 AND a0."Name" = 'Great spotted kiwi' + ORDER BY a0."Name" NULLS FIRST + LIMIT @__p_1 OFFSET @__p_0 + ) AS t + WHERE t."Id" = a."Id") +"""); } public override async Task Update_where_hierarchy(bool async) @@ -79,9 +132,11 @@ public override async Task Update_where_hierarchy(bool async) await base.Update_where_hierarchy(async); AssertExecuteUpdateSql( - @"UPDATE ""Animals"" AS a - SET ""Name"" = 'Animal' -WHERE a.""CountryId"" = 1 AND a.""Name"" = 'Great spotted kiwi'"); +""" +UPDATE "Animals" AS a +SET "Name" = 'Animal' +WHERE a."CountryId" = 1 AND a."Name" = 'Great spotted kiwi' +"""); } public override async Task Update_where_hierarchy_subquery(bool async) @@ -96,9 +151,11 @@ public override async Task Update_where_hierarchy_derived(bool async) await base.Update_where_hierarchy_derived(async); AssertExecuteUpdateSql( - @"UPDATE ""Animals"" AS a - SET ""Name"" = 'Kiwi' -WHERE a.""Discriminator"" = 'Kiwi' AND a.""CountryId"" = 1 AND a.""Name"" = 'Great spotted kiwi'"); +""" +UPDATE "Animals" AS a +SET "Name" = 'Kiwi' +WHERE a."Discriminator" = 'Kiwi' AND a."CountryId" = 1 AND a."Name" = 'Great spotted kiwi' +"""); } public override async Task Update_where_using_hierarchy(bool async) @@ -106,12 +163,14 @@ public override async Task Update_where_using_hierarchy(bool async) await base.Update_where_using_hierarchy(async); AssertExecuteUpdateSql( - @"UPDATE ""Countries"" AS c - SET ""Name"" = 'Monovia' +""" +UPDATE "Countries" AS c +SET "Name" = 'Monovia' WHERE ( SELECT count(*)::int - FROM ""Animals"" AS a - WHERE a.""CountryId"" = 1 AND c.""Id"" = a.""CountryId"" AND a.""CountryId"" > 0) > 0"); + FROM "Animals" AS a + WHERE a."CountryId" = 1 AND c."Id" = a."CountryId" AND a."CountryId" > 0) > 0 +"""); } public override async Task Update_where_using_hierarchy_derived(bool async) @@ -119,12 +178,14 @@ public override async Task Update_where_using_hierarchy_derived(bool async) await base.Update_where_using_hierarchy_derived(async); AssertExecuteUpdateSql( - @"UPDATE ""Countries"" AS c - SET ""Name"" = 'Monovia' +""" +UPDATE "Countries" AS c +SET "Name" = 'Monovia' WHERE ( SELECT count(*)::int - FROM ""Animals"" AS a - WHERE a.""CountryId"" = 1 AND c.""Id"" = a.""CountryId"" AND a.""Discriminator"" = 'Kiwi' AND a.""CountryId"" > 0) > 0"); + FROM "Animals" AS a + WHERE a."CountryId" = 1 AND c."Id" = a."CountryId" AND a."Discriminator" = 'Kiwi' AND a."CountryId" > 0) > 0 +"""); } public override async Task Update_where_keyless_entity_mapped_to_sql_query(bool async) diff --git a/test/EFCore.PG.FunctionalTests/BulkUpdates/InheritanceBulkUpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BulkUpdates/InheritanceBulkUpdatesNpgsqlTest.cs index e61f0701..de9f2fc5 100644 --- a/test/EFCore.PG.FunctionalTests/BulkUpdates/InheritanceBulkUpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BulkUpdates/InheritanceBulkUpdatesNpgsqlTest.cs @@ -4,10 +4,13 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.BulkUpdates; public class InheritanceBulkUpdatesNpgsqlTest : InheritanceBulkUpdatesTestBase { - public InheritanceBulkUpdatesNpgsqlTest(InheritanceBulkUpdatesNpgsqlFixture fixture) + public InheritanceBulkUpdatesNpgsqlTest( + InheritanceBulkUpdatesNpgsqlFixture fixture, + ITestOutputHelper testOutputHelper) : base(fixture) { ClearLog(); + // Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } public override async Task Delete_where_hierarchy(bool async) @@ -71,17 +74,80 @@ public override async Task Delete_where_hierarchy_subquery(bool async) { await base.Delete_where_hierarchy_subquery(async); + AssertSql( +""" +@__p_1='3' +@__p_0='0' + +DELETE FROM "Animals" AS a +WHERE EXISTS ( + SELECT 1 + FROM ( + SELECT a0."Id", a0."CountryId", a0."Discriminator", a0."Name", a0."Species", a0."EagleId", a0."IsFlightless", a0."Group", a0."FoundOn" + FROM "Animals" AS a0 + WHERE a0."Name" = 'Great spotted kiwi' + ORDER BY a0."Name" NULLS FIRST + LIMIT @__p_1 OFFSET @__p_0 + ) AS t + WHERE t."Id" = a."Id") +"""); + } + + public override async Task Delete_GroupBy_Where_Select_First(bool async) + { + await base.Delete_GroupBy_Where_Select_First(async); + + AssertSql( +""" +DELETE FROM "Animals" AS a +WHERE EXISTS ( + SELECT 1 + FROM "Animals" AS a0 + GROUP BY a0."CountryId" + HAVING count(*)::int < 3 AND ( + SELECT a1."Id" + FROM "Animals" AS a1 + WHERE a0."CountryId" = a1."CountryId" + LIMIT 1) = a."Id") +"""); + } + + public override async Task Delete_GroupBy_Where_Select_First_2(bool async) + { + await base.Delete_GroupBy_Where_Select_First_2(async); + AssertSql(); } + public override async Task Delete_GroupBy_Where_Select_First_3(bool async) + { + await base.Delete_GroupBy_Where_Select_First_3(async); + + AssertSql( +""" +DELETE FROM "Animals" AS a +WHERE EXISTS ( + SELECT 1 + FROM "Animals" AS a0 + GROUP BY a0."CountryId" + HAVING count(*)::int < 3 AND ( + SELECT a1."Id" + FROM "Animals" AS a1 + WHERE a0."CountryId" = a1."CountryId" + LIMIT 1) = a."Id") +"""); + } + public override async Task Update_where_hierarchy(bool async) { await base.Update_where_hierarchy(async); AssertExecuteUpdateSql( - @"UPDATE ""Animals"" AS a - SET ""Name"" = 'Animal' -WHERE a.""Name"" = 'Great spotted kiwi'"); +""" +UPDATE "Animals" AS a +SET "Name" = 'Animal' +WHERE a."Name" = 'Great spotted kiwi' +"""); } public override async Task Update_where_hierarchy_subquery(bool async) @@ -96,9 +162,11 @@ public override async Task Update_where_hierarchy_derived(bool async) await base.Update_where_hierarchy_derived(async); AssertExecuteUpdateSql( - @"UPDATE ""Animals"" AS a - SET ""Name"" = 'Kiwi' -WHERE a.""Discriminator"" = 'Kiwi' AND a.""Name"" = 'Great spotted kiwi'"); +""" +UPDATE "Animals" AS a +SET "Name" = 'Kiwi' +WHERE a."Discriminator" = 'Kiwi' AND a."Name" = 'Great spotted kiwi' +"""); } public override async Task Update_where_using_hierarchy(bool async) @@ -106,12 +174,14 @@ public override async Task Update_where_using_hierarchy(bool async) await base.Update_where_using_hierarchy(async); AssertExecuteUpdateSql( - @"UPDATE ""Countries"" AS c - SET ""Name"" = 'Monovia' +""" +UPDATE "Countries" AS c +SET "Name" = 'Monovia' WHERE ( SELECT count(*)::int - FROM ""Animals"" AS a - WHERE c.""Id"" = a.""CountryId"" AND a.""CountryId"" > 0) > 0"); + FROM "Animals" AS a + WHERE c."Id" = a."CountryId" AND a."CountryId" > 0) > 0 +"""); } public override async Task Update_where_using_hierarchy_derived(bool async) @@ -119,12 +189,14 @@ public override async Task Update_where_using_hierarchy_derived(bool async) await base.Update_where_using_hierarchy_derived(async); AssertExecuteUpdateSql( - @"UPDATE ""Countries"" AS c - SET ""Name"" = 'Monovia' +""" +UPDATE "Countries" AS c +SET "Name" = 'Monovia' WHERE ( SELECT count(*)::int - FROM ""Animals"" AS a - WHERE c.""Id"" = a.""CountryId"" AND a.""Discriminator"" = 'Kiwi' AND a.""CountryId"" > 0) > 0"); + FROM "Animals" AS a + WHERE c."Id" = a."CountryId" AND a."Discriminator" = 'Kiwi' AND a."CountryId" > 0) > 0 +"""); } public override async Task Update_where_keyless_entity_mapped_to_sql_query(bool async) diff --git a/test/EFCore.PG.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesNpgsqlTest.cs index e2276101..bc88b240 100644 --- a/test/EFCore.PG.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesNpgsqlTest.cs @@ -14,6 +14,33 @@ public class NonSharedModelBulkUpdatesSqlServerTest : NonSharedModelBulkUpdatesT public virtual void Check_all_tests_overridden() => TestHelpers.AssertAllMethodsOverridden(GetType()); + public override async Task Delete_aggregate_root_when_eager_loaded_owned_collection(bool async) + { + await base.Delete_aggregate_root_when_eager_loaded_owned_collection(async); + + AssertSql( +""" +DELETE FROM "Owner" AS o +"""); + } + + public override async Task Delete_aggregate_root_when_table_sharing_with_owned(bool async) + { + await base.Delete_aggregate_root_when_table_sharing_with_owned(async); + + AssertSql( +""" +DELETE FROM "Owner" AS o +"""); + } + + public override async Task Delete_aggregate_root_when_table_sharing_with_non_owned_throws(bool async) + { + await base.Delete_aggregate_root_when_table_sharing_with_non_owned_throws(async); + + AssertSql(); + } + public override async Task Delete_predicate_based_on_optional_navigation(bool async) { await base.Delete_predicate_based_on_optional_navigation(async); diff --git a/test/EFCore.PG.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesNpgsqlTest.cs index 435bc8cb..5da8226d 100644 --- a/test/EFCore.PG.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesNpgsqlTest.cs @@ -13,6 +13,19 @@ public class NorthwindBulkUpdatesNpgsqlTest : NorthwindBulkUpdatesTestBase 9 AND ( + SELECT o2."OrderID" + FROM "Orders" AS o2 + WHERE o1."CustomerID" = o2."CustomerID" OR ((o1."CustomerID" IS NULL) AND (o2."CustomerID" IS NULL)) + LIMIT 1) = o0."OrderID") +"""); } public override async Task Delete_GroupBy_Where_Select(bool async) @@ -219,6 +245,13 @@ public override async Task Delete_GroupBy_Where_Select(bool async) AssertSql(); } + public override async Task Delete_GroupBy_Where_Select_2(bool async) + { + await base.Delete_GroupBy_Where_Select_2(async); + + AssertSql(); + } + public override async Task Delete_Where_Skip_Take_Skip_Take_causing_subquery(bool async) { await base.Delete_Where_Skip_Take_Skip_Take_causing_subquery(async); @@ -567,6 +600,20 @@ SELECT 1 """); } + public override async Task Update_Where_set_constant_TagWith(bool async) + { + await base.Update_Where_set_constant_TagWith(async); + + AssertExecuteUpdateSql( +""" +-- MyUpdate + +UPDATE "Customers" AS c +SET "ContactName" = 'Updated' +WHERE c."CustomerID" LIKE 'F%' +"""); + } + public override async Task Update_Where_set_constant(bool async) { await base.Update_Where_set_constant(async); @@ -574,7 +621,7 @@ public override async Task Update_Where_set_constant(bool async) AssertExecuteUpdateSql( """ UPDATE "Customers" AS c - SET "ContactName" = 'Updated' +SET "ContactName" = 'Updated' WHERE c."CustomerID" LIKE 'F%' """); } @@ -588,7 +635,7 @@ public override async Task Update_Where_parameter_set_constant(bool async) @__customer_0='ALFKI' UPDATE "Customers" AS c - SET "ContactName" = 'Updated' +SET "ContactName" = 'Updated' WHERE c."CustomerID" = @__customer_0 """, // @@ -608,7 +655,7 @@ WHERE FALSE // """ UPDATE "Customers" AS c - SET "ContactName" = 'Updated' +SET "ContactName" = 'Updated' WHERE FALSE """); } @@ -622,7 +669,7 @@ public override async Task Update_Where_set_parameter(bool async) @__value_0='Abc' UPDATE "Customers" AS c - SET "ContactName" = @__value_0 +SET "ContactName" = @__value_0 WHERE c."CustomerID" LIKE 'F%' """); } @@ -646,7 +693,7 @@ public override async Task Update_Where_OrderBy_set_constant(bool async) AssertExecuteUpdateSql( """ UPDATE "Customers" AS c - SET "ContactName" = 'Updated' +SET "ContactName" = 'Updated' FROM ( SELECT c0."CustomerID", c0."Address", c0."City", c0."CompanyName", c0."ContactName", c0."ContactTitle", c0."Country", c0."Fax", c0."Phone", c0."PostalCode", c0."Region" FROM "Customers" AS c0 @@ -665,7 +712,7 @@ public override async Task Update_Where_OrderBy_Skip_set_constant(bool async) @__p_0='4' UPDATE "Customers" AS c - SET "ContactName" = 'Updated' +SET "ContactName" = 'Updated' FROM ( SELECT c0."CustomerID", c0."Address", c0."City", c0."CompanyName", c0."ContactName", c0."ContactTitle", c0."Country", c0."Fax", c0."Phone", c0."PostalCode", c0."Region" FROM "Customers" AS c0 @@ -686,7 +733,7 @@ public override async Task Update_Where_OrderBy_Take_set_constant(bool async) @__p_0='4' UPDATE "Customers" AS c - SET "ContactName" = 'Updated' +SET "ContactName" = 'Updated' FROM ( SELECT c0."CustomerID", c0."Address", c0."City", c0."CompanyName", c0."ContactName", c0."ContactTitle", c0."Country", c0."Fax", c0."Phone", c0."PostalCode", c0."Region" FROM "Customers" AS c0 @@ -708,7 +755,7 @@ public override async Task Update_Where_OrderBy_Skip_Take_set_constant(bool asyn @__p_0='2' UPDATE "Customers" AS c - SET "ContactName" = 'Updated' +SET "ContactName" = 'Updated' FROM ( SELECT c0."CustomerID", c0."Address", c0."City", c0."CompanyName", c0."ContactName", c0."ContactTitle", c0."Country", c0."Fax", c0."Phone", c0."PostalCode", c0."Region" FROM "Customers" AS c0 @@ -730,7 +777,7 @@ public override async Task Update_Where_OrderBy_Skip_Take_Skip_Take_set_constant @__p_0='2' UPDATE "Customers" AS c - SET "ContactName" = 'Updated' +SET "ContactName" = 'Updated' FROM ( SELECT t."CustomerID", t."Address", t."City", t."CompanyName", t."ContactName", t."ContactTitle", t."Country", t."Fax", t."Phone", t."PostalCode", t."Region" FROM ( @@ -754,7 +801,7 @@ public override async Task Update_Where_GroupBy_aggregate_set_constant(bool asyn AssertExecuteUpdateSql( """ UPDATE "Customers" AS c - SET "ContactName" = 'Updated' +SET "ContactName" = 'Updated' WHERE c."CustomerID" = ( SELECT o."CustomerID" FROM "Orders" AS o @@ -771,7 +818,7 @@ public override async Task Update_Where_GroupBy_First_set_constant(bool async) AssertExecuteUpdateSql( """ UPDATE "Customers" AS c - SET "ContactName" = 'Updated' +SET "ContactName" = 'Updated' WHERE c."CustomerID" = ( SELECT ( SELECT o0."CustomerID" @@ -796,7 +843,21 @@ public override async Task Update_Where_GroupBy_First_set_constant_3(bool async) { await base.Update_Where_GroupBy_First_set_constant_3(async); - AssertExecuteUpdateSql(); + AssertExecuteUpdateSql( +""" +UPDATE "Customers" AS c +SET "ContactName" = 'Updated' +WHERE EXISTS ( + SELECT 1 + FROM "Orders" AS o + GROUP BY o."CustomerID" + HAVING count(*)::int > 11 AND ( + SELECT c0."CustomerID" + FROM "Orders" AS o0 + LEFT JOIN "Customers" AS c0 ON o0."CustomerID" = c0."CustomerID" + WHERE o."CustomerID" = o0."CustomerID" OR ((o."CustomerID" IS NULL) AND (o0."CustomerID" IS NULL)) + LIMIT 1) = c."CustomerID") +"""); } public override async Task Update_Where_Distinct_set_constant(bool async) @@ -806,7 +867,7 @@ public override async Task Update_Where_Distinct_set_constant(bool async) AssertExecuteUpdateSql( """ UPDATE "Customers" AS c - SET "ContactName" = 'Updated' +SET "ContactName" = 'Updated' WHERE c."CustomerID" LIKE 'F%' """); } @@ -818,7 +879,7 @@ public override async Task Update_Where_using_navigation_set_null(bool async) AssertExecuteUpdateSql( """ UPDATE "Orders" AS o - SET "OrderDate" = NULL +SET "OrderDate" = NULL FROM ( SELECT o0."OrderID", o0."CustomerID", o0."EmployeeID", o0."OrderDate", c."CustomerID" AS "CustomerID0" FROM "Orders" AS o0 @@ -836,15 +897,10 @@ public override async Task Update_Where_using_navigation_2_set_constant(bool asy AssertExecuteUpdateSql( """ UPDATE "Order Details" AS o - SET "Quantity" = 1::smallint -FROM ( - SELECT o0."OrderID", o0."ProductID", o0."Discount", o0."Quantity", o0."UnitPrice", o1."OrderID" AS "OrderID0", c."CustomerID" - FROM "Order Details" AS o0 - INNER JOIN "Orders" AS o1 ON o0."OrderID" = o1."OrderID" - LEFT JOIN "Customers" AS c ON o1."CustomerID" = c."CustomerID" - WHERE c."City" = 'Seattle' -) AS t -WHERE o."OrderID" = t."OrderID" AND o."ProductID" = t."ProductID" +SET "Quantity" = 1::smallint +FROM "Orders" AS o0 +LEFT JOIN "Customers" AS c ON o0."CustomerID" = c."CustomerID" +WHERE o."OrderID" = o0."OrderID" AND c."City" = 'Seattle' """); } @@ -855,7 +911,7 @@ public override async Task Update_Where_SelectMany_set_null(bool async) AssertExecuteUpdateSql( """ UPDATE "Orders" AS o - SET "OrderDate" = NULL +SET "OrderDate" = NULL FROM "Customers" AS c WHERE c."CustomerID" = o."CustomerID" AND (c."CustomerID" LIKE 'F%') """); @@ -868,7 +924,7 @@ public override async Task Update_Where_set_property_plus_constant(bool async) AssertExecuteUpdateSql( """ UPDATE "Customers" AS c - SET "ContactName" = COALESCE(c."ContactName", '') || 'Abc' +SET "ContactName" = COALESCE(c."ContactName", '') || 'Abc' WHERE c."CustomerID" LIKE 'F%' """); } @@ -882,7 +938,7 @@ public override async Task Update_Where_set_property_plus_parameter(bool async) @__value_0='Abc' UPDATE "Customers" AS c - SET "ContactName" = COALESCE(c."ContactName", '') || @__value_0 +SET "ContactName" = COALESCE(c."ContactName", '') || @__value_0 WHERE c."CustomerID" LIKE 'F%' """); } @@ -894,7 +950,7 @@ public override async Task Update_Where_set_property_plus_property(bool async) AssertExecuteUpdateSql( """ UPDATE "Customers" AS c - SET "ContactName" = COALESCE(c."ContactName", '') || c."CustomerID" +SET "ContactName" = COALESCE(c."ContactName", '') || c."CustomerID" WHERE c."CustomerID" LIKE 'F%' """); } @@ -906,7 +962,7 @@ public override async Task Update_Where_set_constant_using_ef_property(bool asyn AssertExecuteUpdateSql( """ UPDATE "Customers" AS c - SET "ContactName" = 'Updated' +SET "ContactName" = 'Updated' WHERE c."CustomerID" LIKE 'F%' """); } @@ -918,7 +974,7 @@ public override async Task Update_Where_set_null(bool async) AssertExecuteUpdateSql( """ UPDATE "Customers" AS c - SET "ContactName" = NULL +SET "ContactName" = NULL WHERE c."CustomerID" LIKE 'F%' """); } @@ -946,7 +1002,7 @@ public override async Task Update_Where_multiple_set(bool async) @__value_0='Abc' UPDATE "Customers" AS c - SET "City" = 'Seattle', +SET "City" = 'Seattle', "ContactName" = @__value_0 WHERE c."CustomerID" LIKE 'F%' """); @@ -978,18 +1034,20 @@ public override async Task Update_Union_set_constant(bool async) await base.Update_Union_set_constant(async); AssertExecuteUpdateSql( - @"UPDATE ""Customers"" AS c - SET ""ContactName"" = 'Updated' +""" +UPDATE "Customers" AS c +SET "ContactName" = 'Updated' FROM ( - SELECT c0.""CustomerID"", c0.""Address"", c0.""City"", c0.""CompanyName"", c0.""ContactName"", c0.""ContactTitle"", c0.""Country"", c0.""Fax"", c0.""Phone"", c0.""PostalCode"", c0.""Region"" - FROM ""Customers"" AS c0 - WHERE c0.""CustomerID"" LIKE 'F%' + SELECT c0."CustomerID", c0."Address", c0."City", c0."CompanyName", c0."ContactName", c0."ContactTitle", c0."Country", c0."Fax", c0."Phone", c0."PostalCode", c0."Region" + FROM "Customers" AS c0 + WHERE c0."CustomerID" LIKE 'F%' UNION - SELECT c1.""CustomerID"", c1.""Address"", c1.""City"", c1.""CompanyName"", c1.""ContactName"", c1.""ContactTitle"", c1.""Country"", c1.""Fax"", c1.""Phone"", c1.""PostalCode"", c1.""Region"" - FROM ""Customers"" AS c1 - WHERE c1.""CustomerID"" LIKE 'A%' + SELECT c1."CustomerID", c1."Address", c1."City", c1."CompanyName", c1."ContactName", c1."ContactTitle", c1."Country", c1."Fax", c1."Phone", c1."PostalCode", c1."Region" + FROM "Customers" AS c1 + WHERE c1."CustomerID" LIKE 'A%' ) AS t -WHERE c.""CustomerID"" = t.""CustomerID"""); +WHERE c."CustomerID" = t."CustomerID" +"""); } public override async Task Update_Concat_set_constant(bool async) @@ -997,18 +1055,20 @@ public override async Task Update_Concat_set_constant(bool async) await base.Update_Concat_set_constant(async); AssertExecuteUpdateSql( - @"UPDATE ""Customers"" AS c - SET ""ContactName"" = 'Updated' +""" +UPDATE "Customers" AS c +SET "ContactName" = 'Updated' FROM ( - SELECT c0.""CustomerID"", c0.""Address"", c0.""City"", c0.""CompanyName"", c0.""ContactName"", c0.""ContactTitle"", c0.""Country"", c0.""Fax"", c0.""Phone"", c0.""PostalCode"", c0.""Region"" - FROM ""Customers"" AS c0 - WHERE c0.""CustomerID"" LIKE 'F%' + SELECT c0."CustomerID", c0."Address", c0."City", c0."CompanyName", c0."ContactName", c0."ContactTitle", c0."Country", c0."Fax", c0."Phone", c0."PostalCode", c0."Region" + FROM "Customers" AS c0 + WHERE c0."CustomerID" LIKE 'F%' UNION ALL - SELECT c1.""CustomerID"", c1.""Address"", c1.""City"", c1.""CompanyName"", c1.""ContactName"", c1.""ContactTitle"", c1.""Country"", c1.""Fax"", c1.""Phone"", c1.""PostalCode"", c1.""Region"" - FROM ""Customers"" AS c1 - WHERE c1.""CustomerID"" LIKE 'A%' + SELECT c1."CustomerID", c1."Address", c1."City", c1."CompanyName", c1."ContactName", c1."ContactTitle", c1."Country", c1."Fax", c1."Phone", c1."PostalCode", c1."Region" + FROM "Customers" AS c1 + WHERE c1."CustomerID" LIKE 'A%' ) AS t -WHERE c.""CustomerID"" = t.""CustomerID"""); +WHERE c."CustomerID" = t."CustomerID" +"""); } public override async Task Update_Except_set_constant(bool async) @@ -1016,18 +1076,20 @@ public override async Task Update_Except_set_constant(bool async) await base.Update_Except_set_constant(async); AssertExecuteUpdateSql( - @"UPDATE ""Customers"" AS c - SET ""ContactName"" = 'Updated' +""" +UPDATE "Customers" AS c +SET "ContactName" = 'Updated' FROM ( - SELECT c0.""CustomerID"", c0.""Address"", c0.""City"", c0.""CompanyName"", c0.""ContactName"", c0.""ContactTitle"", c0.""Country"", c0.""Fax"", c0.""Phone"", c0.""PostalCode"", c0.""Region"" - FROM ""Customers"" AS c0 - WHERE c0.""CustomerID"" LIKE 'F%' + SELECT c0."CustomerID", c0."Address", c0."City", c0."CompanyName", c0."ContactName", c0."ContactTitle", c0."Country", c0."Fax", c0."Phone", c0."PostalCode", c0."Region" + FROM "Customers" AS c0 + WHERE c0."CustomerID" LIKE 'F%' EXCEPT - SELECT c1.""CustomerID"", c1.""Address"", c1.""City"", c1.""CompanyName"", c1.""ContactName"", c1.""ContactTitle"", c1.""Country"", c1.""Fax"", c1.""Phone"", c1.""PostalCode"", c1.""Region"" - FROM ""Customers"" AS c1 - WHERE c1.""CustomerID"" LIKE 'A%' + SELECT c1."CustomerID", c1."Address", c1."City", c1."CompanyName", c1."ContactName", c1."ContactTitle", c1."Country", c1."Fax", c1."Phone", c1."PostalCode", c1."Region" + FROM "Customers" AS c1 + WHERE c1."CustomerID" LIKE 'A%' ) AS t -WHERE c.""CustomerID"" = t.""CustomerID"""); +WHERE c."CustomerID" = t."CustomerID" +"""); } public override async Task Update_Intersect_set_constant(bool async) @@ -1035,18 +1097,20 @@ public override async Task Update_Intersect_set_constant(bool async) await base.Update_Intersect_set_constant(async); AssertExecuteUpdateSql( - @"UPDATE ""Customers"" AS c - SET ""ContactName"" = 'Updated' +""" +UPDATE "Customers" AS c +SET "ContactName" = 'Updated' FROM ( - SELECT c0.""CustomerID"", c0.""Address"", c0.""City"", c0.""CompanyName"", c0.""ContactName"", c0.""ContactTitle"", c0.""Country"", c0.""Fax"", c0.""Phone"", c0.""PostalCode"", c0.""Region"" - FROM ""Customers"" AS c0 - WHERE c0.""CustomerID"" LIKE 'F%' + SELECT c0."CustomerID", c0."Address", c0."City", c0."CompanyName", c0."ContactName", c0."ContactTitle", c0."Country", c0."Fax", c0."Phone", c0."PostalCode", c0."Region" + FROM "Customers" AS c0 + WHERE c0."CustomerID" LIKE 'F%' INTERSECT - SELECT c1.""CustomerID"", c1.""Address"", c1.""City"", c1.""CompanyName"", c1.""ContactName"", c1.""ContactTitle"", c1.""Country"", c1.""Fax"", c1.""Phone"", c1.""PostalCode"", c1.""Region"" - FROM ""Customers"" AS c1 - WHERE c1.""CustomerID"" LIKE 'A%' + SELECT c1."CustomerID", c1."Address", c1."City", c1."CompanyName", c1."ContactName", c1."ContactTitle", c1."Country", c1."Fax", c1."Phone", c1."PostalCode", c1."Region" + FROM "Customers" AS c1 + WHERE c1."CustomerID" LIKE 'A%' ) AS t -WHERE c.""CustomerID"" = t.""CustomerID"""); +WHERE c."CustomerID" = t."CustomerID" +"""); } public override async Task Update_with_join_set_constant(bool async) @@ -1054,14 +1118,16 @@ public override async Task Update_with_join_set_constant(bool async) await base.Update_with_join_set_constant(async); AssertExecuteUpdateSql( - @"UPDATE ""Customers"" AS c - SET ""ContactName"" = 'Updated' +""" +UPDATE "Customers" AS c +SET "ContactName" = 'Updated' FROM ( - SELECT o.""OrderID"", o.""CustomerID"", o.""EmployeeID"", o.""OrderDate"" - FROM ""Orders"" AS o - WHERE o.""OrderID"" < 10300 + SELECT o."OrderID", o."CustomerID", o."EmployeeID", o."OrderDate" + FROM "Orders" AS o + WHERE o."OrderID" < 10300 ) AS t -WHERE c.""CustomerID"" = t.""CustomerID"" AND (c.""CustomerID"" LIKE 'F%')"); +WHERE c."CustomerID" = t."CustomerID" AND (c."CustomerID" LIKE 'F%') +"""); } public override async Task Update_with_left_join_set_constant(bool async) @@ -1069,19 +1135,21 @@ public override async Task Update_with_left_join_set_constant(bool async) await base.Update_with_left_join_set_constant(async); AssertExecuteUpdateSql( - @"UPDATE ""Customers"" AS c - SET ""ContactName"" = 'Updated' +""" +UPDATE "Customers" AS c +SET "ContactName" = 'Updated' FROM ( - SELECT c0.""CustomerID"", c0.""Address"", c0.""City"", c0.""CompanyName"", c0.""ContactName"", c0.""ContactTitle"", c0.""Country"", c0.""Fax"", c0.""Phone"", c0.""PostalCode"", c0.""Region"", t.""OrderID"", t.""CustomerID"" AS ""CustomerID0"", t.""EmployeeID"", t.""OrderDate"" - FROM ""Customers"" AS c0 + SELECT c0."CustomerID", c0."Address", c0."City", c0."CompanyName", c0."ContactName", c0."ContactTitle", c0."Country", c0."Fax", c0."Phone", c0."PostalCode", c0."Region", t."OrderID", t."CustomerID" AS "CustomerID0", t."EmployeeID", t."OrderDate" + FROM "Customers" AS c0 LEFT JOIN ( - SELECT o.""OrderID"", o.""CustomerID"", o.""EmployeeID"", o.""OrderDate"" - FROM ""Orders"" AS o - WHERE o.""OrderID"" < 10300 - ) AS t ON c0.""CustomerID"" = t.""CustomerID"" - WHERE c0.""CustomerID"" LIKE 'F%' + SELECT o."OrderID", o."CustomerID", o."EmployeeID", o."OrderDate" + FROM "Orders" AS o + WHERE o."OrderID" < 10300 + ) AS t ON c0."CustomerID" = t."CustomerID" + WHERE c0."CustomerID" LIKE 'F%' ) AS t0 -WHERE c.""CustomerID"" = t0.""CustomerID"""); +WHERE c."CustomerID" = t0."CustomerID" +"""); } public override async Task Update_with_cross_join_set_constant(bool async) @@ -1089,17 +1157,18 @@ public override async Task Update_with_cross_join_set_constant(bool async) await base.Update_with_cross_join_set_constant(async); AssertExecuteUpdateSql( - @"UPDATE ""Customers"" AS c - SET ""ContactName"" = 'Updated' +""" +UPDATE "Customers" AS c +SET "ContactName" = 'Updated' FROM ( - SELECT o.""OrderID"", o.""CustomerID"", o.""EmployeeID"", o.""OrderDate"" - FROM ""Orders"" AS o - WHERE o.""OrderID"" < 10300 + SELECT o."OrderID", o."CustomerID", o."EmployeeID", o."OrderDate" + FROM "Orders" AS o + WHERE o."OrderID" < 10300 ) AS t -WHERE c.""CustomerID"" LIKE 'F%'"); +WHERE c."CustomerID" LIKE 'F%' +"""); } - [ConditionalTheory(Skip = "invalid reference to FROM-clause entry for table c")] public override async Task Update_with_cross_apply_set_constant(bool async) { await base.Update_with_cross_apply_set_constant(async); @@ -1107,13 +1176,18 @@ public override async Task Update_with_cross_apply_set_constant(bool async) AssertExecuteUpdateSql( """ UPDATE "Customers" AS c - SET "ContactName" = 'Updated' +SET "ContactName" = 'Updated' FROM ( - SELECT o."OrderID", o."CustomerID", o."EmployeeID", o."OrderDate" - FROM "Orders" AS o - WHERE o."OrderID" < 10300 AND date_part('year', o."OrderDate")::int < length(c."ContactName")::int -) AS t -WHERE c."CustomerID" LIKE 'F%' + SELECT c0."CustomerID", c0."Address", c0."City", c0."CompanyName", c0."ContactName", c0."ContactTitle", c0."Country", c0."Fax", c0."Phone", c0."PostalCode", c0."Region", t."OrderID", t."CustomerID" AS "CustomerID0", t."EmployeeID", t."OrderDate" + FROM "Customers" AS c0 + JOIN LATERAL ( + SELECT o."OrderID", o."CustomerID", o."EmployeeID", o."OrderDate" + FROM "Orders" AS o + WHERE o."OrderID" < 10300 AND date_part('year', o."OrderDate")::int < length(c0."ContactName")::int + ) AS t ON TRUE + WHERE c0."CustomerID" LIKE 'F%' +) AS t0 +WHERE c."CustomerID" = t0."CustomerID" """); } @@ -1122,19 +1196,87 @@ public override async Task Update_with_outer_apply_set_constant(bool async) await base.Update_with_outer_apply_set_constant(async); AssertExecuteUpdateSql( - @"UPDATE ""Customers"" AS c - SET ""ContactName"" = 'Updated' +""" +UPDATE "Customers" AS c +SET "ContactName" = 'Updated' FROM ( - SELECT c0.""CustomerID"", c0.""Address"", c0.""City"", c0.""CompanyName"", c0.""ContactName"", c0.""ContactTitle"", c0.""Country"", c0.""Fax"", c0.""Phone"", c0.""PostalCode"", c0.""Region"", t.""OrderID"", t.""CustomerID"" AS ""CustomerID0"", t.""EmployeeID"", t.""OrderDate"" - FROM ""Customers"" AS c0 + SELECT c0."CustomerID", c0."Address", c0."City", c0."CompanyName", c0."ContactName", c0."ContactTitle", c0."Country", c0."Fax", c0."Phone", c0."PostalCode", c0."Region", t."OrderID", t."CustomerID" AS "CustomerID0", t."EmployeeID", t."OrderDate" + FROM "Customers" AS c0 LEFT JOIN LATERAL ( - SELECT o.""OrderID"", o.""CustomerID"", o.""EmployeeID"", o.""OrderDate"" - FROM ""Orders"" AS o - WHERE o.""OrderID"" < 10300 AND date_part('year', o.""OrderDate"")::int < length(c0.""ContactName"")::int + SELECT o."OrderID", o."CustomerID", o."EmployeeID", o."OrderDate" + FROM "Orders" AS o + WHERE o."OrderID" < 10300 AND date_part('year', o."OrderDate")::int < length(c0."ContactName")::int ) AS t ON TRUE - WHERE c0.""CustomerID"" LIKE 'F%' + WHERE c0."CustomerID" LIKE 'F%' ) AS t0 -WHERE c.""CustomerID"" = t0.""CustomerID"""); +WHERE c."CustomerID" = t0."CustomerID" +"""); + } + + [ConditionalTheory(Skip = "https://github.com/npgsql/efcore.pg/issues/2478")] + public override async Task Update_with_cross_join_left_join_set_constant(bool async) + { + await base.Update_with_cross_join_left_join_set_constant(async); + + AssertExecuteUpdateSql( + @"UPDATE [c] +SET [c].[ContactName] = N'Updated' +FROM [Customers] AS [c] +CROSS JOIN ( + SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region] + FROM [Customers] AS [c0] + WHERE [c0].[City] IS NOT NULL AND ([c0].[City] LIKE N'S%') +) AS [t] +LEFT JOIN ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] + FROM [Orders] AS [o] + WHERE [o].[OrderID] < 10300 +) AS [t0] ON [c].[CustomerID] = [t0].[CustomerID] +WHERE [c].[CustomerID] LIKE N'F%'"); + } + + [ConditionalTheory(Skip = "https://github.com/npgsql/efcore.pg/issues/2478")] + public override async Task Update_with_cross_join_cross_apply_set_constant(bool async) + { + await base.Update_with_cross_join_cross_apply_set_constant(async); + + AssertExecuteUpdateSql( + @"UPDATE [c] +SET [c].[ContactName] = N'Updated' +FROM [Customers] AS [c] +CROSS JOIN ( + SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region] + FROM [Customers] AS [c0] + WHERE [c0].[City] IS NOT NULL AND ([c0].[City] LIKE N'S%') +) AS [t] +CROSS APPLY ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] + FROM [Orders] AS [o] + WHERE [o].[OrderID] < 10300 AND DATEPART(year, [o].[OrderDate]) < CAST(LEN([c].[ContactName]) AS int) +) AS [t0] +WHERE [c].[CustomerID] LIKE N'F%'"); + } + + [ConditionalTheory(Skip = "https://github.com/npgsql/efcore.pg/issues/2478")] + public override async Task Update_with_cross_join_outer_apply_set_constant(bool async) + { + await base.Update_with_cross_join_outer_apply_set_constant(async); + + AssertExecuteUpdateSql( + @"UPDATE [c] +SET [c].[ContactName] = N'Updated' +FROM [Customers] AS [c] +CROSS JOIN ( + SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region] + FROM [Customers] AS [c0] + WHERE [c0].[City] IS NOT NULL AND ([c0].[City] LIKE N'S%') +) AS [t] +OUTER APPLY ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] + FROM [Orders] AS [o] + WHERE [o].[OrderID] < 10300 AND DATEPART(year, [o].[OrderDate]) < CAST(LEN([c].[ContactName]) AS int) +) AS [t0] +WHERE [c].[CustomerID] LIKE N'F%'"); } public override async Task Update_FromSql_set_constant(bool async) @@ -1149,26 +1291,38 @@ public override async Task Update_Where_SelectMany_subquery_set_null(bool async) await base.Update_Where_SelectMany_subquery_set_null(async); AssertExecuteUpdateSql( - @"UPDATE ""Orders"" AS o - SET ""OrderDate"" = NULL +""" +UPDATE "Orders" AS o +SET "OrderDate" = NULL FROM ( - SELECT t.""OrderID"", t.""CustomerID"", t.""EmployeeID"", t.""OrderDate"", c.""CustomerID"" AS ""CustomerID0"" - FROM ""Customers"" AS c + SELECT t."OrderID", t."CustomerID", t."EmployeeID", t."OrderDate", c."CustomerID" AS "CustomerID0" + FROM "Customers" AS c INNER JOIN ( - SELECT o0.""OrderID"", o0.""CustomerID"", o0.""EmployeeID"", o0.""OrderDate"" - FROM ""Orders"" AS o0 - WHERE date_part('year', o0.""OrderDate"")::int = 1997 - ) AS t ON c.""CustomerID"" = t.""CustomerID"" - WHERE c.""CustomerID"" LIKE 'F%' + SELECT o0."OrderID", o0."CustomerID", o0."EmployeeID", o0."OrderDate" + FROM "Orders" AS o0 + WHERE date_part('year', o0."OrderDate")::int = 1997 + ) AS t ON c."CustomerID" = t."CustomerID" + WHERE c."CustomerID" LIKE 'F%' ) AS t0 -WHERE o.""OrderID"" = t0.""OrderID"""); +WHERE o."OrderID" = t0."OrderID" +"""); } public override async Task Update_Where_Join_set_property_from_joined_single_result_table(bool async) { await base.Update_Where_Join_set_property_from_joined_single_result_table(async); - AssertExecuteUpdateSql(); + AssertExecuteUpdateSql( +""" +UPDATE "Customers" AS c +SET "City" = date_part('year', ( + SELECT o."OrderDate" + FROM "Orders" AS o + WHERE c."CustomerID" = o."CustomerID" + ORDER BY o."OrderDate" DESC NULLS LAST + LIMIT 1))::int::text +WHERE c."CustomerID" LIKE 'F%' +"""); } public override async Task Update_Where_Join_set_property_from_joined_table(bool async) @@ -1176,14 +1330,16 @@ public override async Task Update_Where_Join_set_property_from_joined_table(bool await base.Update_Where_Join_set_property_from_joined_table(async); AssertExecuteUpdateSql( - @"UPDATE ""Customers"" AS c - SET ""City"" = t.""City"" +""" +UPDATE "Customers" AS c +SET "City" = t."City" FROM ( - SELECT c0.""CustomerID"", c0.""Address"", c0.""City"", c0.""CompanyName"", c0.""ContactName"", c0.""ContactTitle"", c0.""Country"", c0.""Fax"", c0.""Phone"", c0.""PostalCode"", c0.""Region"" - FROM ""Customers"" AS c0 - WHERE c0.""CustomerID"" = 'ALFKI' + SELECT c0."CustomerID", c0."Address", c0."City", c0."CompanyName", c0."ContactName", c0."ContactTitle", c0."Country", c0."Fax", c0."Phone", c0."PostalCode", c0."Region" + FROM "Customers" AS c0 + WHERE c0."CustomerID" = 'ALFKI' ) AS t -WHERE c.""CustomerID"" LIKE 'F%'"); +WHERE c."CustomerID" LIKE 'F%' +"""); } public override async Task Update_Where_Join_set_property_from_joined_single_result_scalar(bool async) @@ -1191,14 +1347,16 @@ public override async Task Update_Where_Join_set_property_from_joined_single_res await base.Update_Where_Join_set_property_from_joined_single_result_scalar(async); AssertExecuteUpdateSql( - @"UPDATE ""Customers"" AS c - SET ""City"" = date_part('year', ( - SELECT o.""OrderDate"" - FROM ""Orders"" AS o - WHERE c.""CustomerID"" = o.""CustomerID"" - ORDER BY o.""OrderDate"" DESC NULLS LAST - LIMIT 1))::int::text -WHERE c.""CustomerID"" LIKE 'F%'"); +""" +UPDATE "Customers" AS c +SET "City" = date_part('year', ( + SELECT o."OrderDate" + FROM "Orders" AS o + WHERE c."CustomerID" = o."CustomerID" + ORDER BY o."OrderDate" DESC NULLS LAST + LIMIT 1))::int::text +WHERE c."CustomerID" LIKE 'F%' +"""); } [ConditionalFact] diff --git a/test/EFCore.PG.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesNpgsqlTest.cs index 61de4334..b41aabb9 100644 --- a/test/EFCore.PG.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesNpgsqlTest.cs @@ -5,10 +5,13 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.BulkUpdates; public class TPCFiltersInheritanceBulkUpdatesNpgsqlTest : TPCFiltersInheritanceBulkUpdatesTestBase { - public TPCFiltersInheritanceBulkUpdatesNpgsqlTest(TPCFiltersInheritanceBulkUpdatesNpgsqlFixture fixture) + public TPCFiltersInheritanceBulkUpdatesNpgsqlTest( + TPCFiltersInheritanceBulkUpdatesNpgsqlFixture fixture, + ITestOutputHelper testOutputHelper) : base(fixture) { ClearLog(); + // Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } public override async Task Delete_where_hierarchy(bool async) @@ -80,7 +83,28 @@ public override async Task Delete_where_hierarchy_subquery(bool async) AssertSql(); } - public override async Task Update_where_hierarchy(bool async) + public override async Task Delete_GroupBy_Where_Select_First(bool async) + { + await base.Delete_GroupBy_Where_Select_First(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_2(bool async) + { + await base.Delete_GroupBy_Where_Select_First_2(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_3(bool async) + { + await base.Delete_GroupBy_Where_Select_First_3(async); + + AssertSql(); + } + + public override async Task Update_where_hierarchy(bool async) { await base.Update_where_hierarchy(async); @@ -99,9 +123,11 @@ public override async Task Update_where_hierarchy_derived(bool async) await base.Update_where_hierarchy_derived(async); AssertExecuteUpdateSql( - @"UPDATE ""Kiwi"" AS k - SET ""Name"" = 'Kiwi' -WHERE k.""CountryId"" = 1 AND k.""Name"" = 'Great spotted kiwi'"); +""" +UPDATE "Kiwi" AS k +SET "Name" = 'Kiwi' +WHERE k."CountryId" = 1 AND k."Name" = 'Great spotted kiwi' +"""); } public override async Task Update_where_using_hierarchy(bool async) @@ -109,18 +135,20 @@ public override async Task Update_where_using_hierarchy(bool async) await base.Update_where_using_hierarchy(async); AssertExecuteUpdateSql( - @"UPDATE ""Countries"" AS c - SET ""Name"" = 'Monovia' +""" +UPDATE "Countries" AS c +SET "Name" = 'Monovia' WHERE ( SELECT count(*)::int FROM ( - SELECT e.""Id"", e.""CountryId"", e.""Name"", e.""Species"", e.""EagleId"", e.""IsFlightless"", e.""Group"", NULL AS ""FoundOn"", 'Eagle' AS ""Discriminator"" - FROM ""Eagle"" AS e + SELECT e."Id", e."CountryId", e."Name", e."Species", e."EagleId", e."IsFlightless", e."Group", NULL AS "FoundOn", 'Eagle' AS "Discriminator" + FROM "Eagle" AS e UNION ALL - SELECT k.""Id"", k.""CountryId"", k.""Name"", k.""Species"", k.""EagleId"", k.""IsFlightless"", NULL AS ""Group"", k.""FoundOn"", 'Kiwi' AS ""Discriminator"" - FROM ""Kiwi"" AS k + SELECT k."Id", k."CountryId", k."Name", k."Species", k."EagleId", k."IsFlightless", NULL AS "Group", k."FoundOn", 'Kiwi' AS "Discriminator" + FROM "Kiwi" AS k ) AS t - WHERE t.""CountryId"" = 1 AND c.""Id"" = t.""CountryId"" AND t.""CountryId"" > 0) > 0"); + WHERE t."CountryId" = 1 AND c."Id" = t."CountryId" AND t."CountryId" > 0) > 0 +"""); } public override async Task Update_where_using_hierarchy_derived(bool async) @@ -128,15 +156,17 @@ public override async Task Update_where_using_hierarchy_derived(bool async) await base.Update_where_using_hierarchy_derived(async); AssertExecuteUpdateSql( - @"UPDATE ""Countries"" AS c - SET ""Name"" = 'Monovia' +""" +UPDATE "Countries" AS c +SET "Name" = 'Monovia' WHERE ( SELECT count(*)::int FROM ( - SELECT k.""Id"", k.""CountryId"", k.""Name"", k.""Species"", k.""EagleId"", k.""IsFlightless"", NULL AS ""Group"", k.""FoundOn"", 'Kiwi' AS ""Discriminator"" - FROM ""Kiwi"" AS k + SELECT k."Id", k."CountryId", k."Name", k."Species", k."EagleId", k."IsFlightless", NULL AS "Group", k."FoundOn", 'Kiwi' AS "Discriminator" + FROM "Kiwi" AS k ) AS t - WHERE t.""CountryId"" = 1 AND c.""Id"" = t.""CountryId"" AND t.""CountryId"" > 0) > 0"); + WHERE t."CountryId" = 1 AND c."Id" = t."CountryId" AND t."CountryId" > 0) > 0 +"""); } public override async Task Update_where_keyless_entity_mapped_to_sql_query(bool async) diff --git a/test/EFCore.PG.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesNpgsqlTest.cs index 694e9e5e..94b13c81 100644 --- a/test/EFCore.PG.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesNpgsqlTest.cs @@ -5,10 +5,13 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.BulkUpdates; public class TPCInheritanceBulkUpdatesNpgsqlTest : TPCInheritanceBulkUpdatesTestBase { - public TPCInheritanceBulkUpdatesNpgsqlTest(TPCInheritanceBulkUpdatesNpgsqlFixture fixture) + public TPCInheritanceBulkUpdatesNpgsqlTest( + TPCInheritanceBulkUpdatesNpgsqlFixture fixture, + ITestOutputHelper testOutputHelper) : base(fixture) { ClearLog(); + // Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } public override async Task Delete_where_hierarchy(bool async) @@ -80,7 +83,28 @@ public override async Task Delete_where_hierarchy_subquery(bool async) AssertSql(); } - public override async Task Update_where_hierarchy(bool async) + public override async Task Delete_GroupBy_Where_Select_First_3(bool async) + { + await base.Delete_GroupBy_Where_Select_First_3(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First(bool async) + { + await base.Delete_GroupBy_Where_Select_First(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_2(bool async) + { + await base.Delete_GroupBy_Where_Select_First_2(async); + + AssertSql(); + } + + public override async Task Update_where_hierarchy(bool async) { await base.Update_where_hierarchy(async); @@ -99,9 +123,11 @@ public override async Task Update_where_hierarchy_derived(bool async) await base.Update_where_hierarchy_derived(async); AssertExecuteUpdateSql( - @"UPDATE ""Kiwi"" AS k - SET ""Name"" = 'Kiwi' -WHERE k.""Name"" = 'Great spotted kiwi'"); +""" +UPDATE "Kiwi" AS k +SET "Name" = 'Kiwi' +WHERE k."Name" = 'Great spotted kiwi' +"""); } public override async Task Update_where_using_hierarchy(bool async) @@ -109,18 +135,20 @@ public override async Task Update_where_using_hierarchy(bool async) await base.Update_where_using_hierarchy(async); AssertExecuteUpdateSql( - @"UPDATE ""Countries"" AS c - SET ""Name"" = 'Monovia' +""" +UPDATE "Countries" AS c +SET "Name" = 'Monovia' WHERE ( SELECT count(*)::int FROM ( - SELECT e.""Id"", e.""CountryId"", e.""Name"", e.""Species"", e.""EagleId"", e.""IsFlightless"", e.""Group"", NULL AS ""FoundOn"", 'Eagle' AS ""Discriminator"" - FROM ""Eagle"" AS e + SELECT e."Id", e."CountryId", e."Name", e."Species", e."EagleId", e."IsFlightless", e."Group", NULL AS "FoundOn", 'Eagle' AS "Discriminator" + FROM "Eagle" AS e UNION ALL - SELECT k.""Id"", k.""CountryId"", k.""Name"", k.""Species"", k.""EagleId"", k.""IsFlightless"", NULL AS ""Group"", k.""FoundOn"", 'Kiwi' AS ""Discriminator"" - FROM ""Kiwi"" AS k + SELECT k."Id", k."CountryId", k."Name", k."Species", k."EagleId", k."IsFlightless", NULL AS "Group", k."FoundOn", 'Kiwi' AS "Discriminator" + FROM "Kiwi" AS k ) AS t - WHERE c.""Id"" = t.""CountryId"" AND t.""CountryId"" > 0) > 0"); + WHERE c."Id" = t."CountryId" AND t."CountryId" > 0) > 0 +"""); } public override async Task Update_where_using_hierarchy_derived(bool async) @@ -128,15 +156,17 @@ public override async Task Update_where_using_hierarchy_derived(bool async) await base.Update_where_using_hierarchy_derived(async); AssertExecuteUpdateSql( - @"UPDATE ""Countries"" AS c - SET ""Name"" = 'Monovia' +""" +UPDATE "Countries" AS c +SET "Name" = 'Monovia' WHERE ( SELECT count(*)::int FROM ( - SELECT k.""Id"", k.""CountryId"", k.""Name"", k.""Species"", k.""EagleId"", k.""IsFlightless"", NULL AS ""Group"", k.""FoundOn"", 'Kiwi' AS ""Discriminator"" - FROM ""Kiwi"" AS k + SELECT k."Id", k."CountryId", k."Name", k."Species", k."EagleId", k."IsFlightless", NULL AS "Group", k."FoundOn", 'Kiwi' AS "Discriminator" + FROM "Kiwi" AS k ) AS t - WHERE c.""Id"" = t.""CountryId"" AND t.""CountryId"" > 0) > 0"); + WHERE c."Id" = t."CountryId" AND t."CountryId" > 0) > 0 +"""); } public override async Task Update_where_keyless_entity_mapped_to_sql_query(bool async) diff --git a/test/EFCore.PG.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesNpgsqlTest.cs index 88329927..b73141b3 100644 --- a/test/EFCore.PG.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesNpgsqlTest.cs @@ -5,10 +5,13 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.BulkUpdates; public class TPTFiltersInheritanceBulkUpdatesSqlServerTest : TPTFiltersInheritanceBulkUpdatesTestBase { - public TPTFiltersInheritanceBulkUpdatesSqlServerTest(TPTFiltersInheritanceBulkUpdatesNpgsqlFixture fixture) + public TPTFiltersInheritanceBulkUpdatesSqlServerTest( + TPTFiltersInheritanceBulkUpdatesNpgsqlFixture fixture, + ITestOutputHelper testOutputHelper) : base(fixture) { ClearLog(); + // Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } public override async Task Delete_where_hierarchy(bool async) @@ -31,12 +34,14 @@ public override async Task Delete_where_using_hierarchy(bool async) AssertSql( """ -DELETE FROM [c] -FROM [Countries] AS [c] +DELETE FROM "Countries" AS c WHERE ( - SELECT COUNT(*) - FROM [Animals] AS [a] - WHERE [a].[CountryId] = 1 AND [c].[Id] = [a].[CountryId] AND [a].[CountryId] > 0) > 0 + SELECT count(*)::int + FROM "Animals" AS a + LEFT JOIN "Birds" AS b ON a."Id" = b."Id" + LEFT JOIN "Eagle" AS e ON a."Id" = e."Id" + LEFT JOIN "Kiwi" AS k ON a."Id" = k."Id" + WHERE a."CountryId" = 1 AND c."Id" = a."CountryId" AND a."CountryId" > 0) > 0 """); } @@ -46,12 +51,14 @@ public override async Task Delete_where_using_hierarchy_derived(bool async) AssertSql( """ -DELETE FROM [c] -FROM [Countries] AS [c] +DELETE FROM "Countries" AS c WHERE ( - SELECT COUNT(*) - FROM [Animals] AS [a] - WHERE [a].[CountryId] = 1 AND [c].[Id] = [a].[CountryId] AND [a].[Discriminator] = N'Kiwi' AND [a].[CountryId] > 0) > 0 + SELECT count(*)::int + FROM "Animals" AS a + LEFT JOIN "Birds" AS b ON a."Id" = b."Id" + LEFT JOIN "Eagle" AS e ON a."Id" = e."Id" + LEFT JOIN "Kiwi" AS k ON a."Id" = k."Id" + WHERE a."CountryId" = 1 AND c."Id" = a."CountryId" AND (k."Id" IS NOT NULL) AND a."CountryId" > 0) > 0 """); } @@ -69,6 +76,27 @@ public override async Task Delete_where_hierarchy_subquery(bool async) AssertSql(); } + public override async Task Delete_GroupBy_Where_Select_First_3(bool async) + { + await base.Delete_GroupBy_Where_Select_First_3(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First(bool async) + { + await base.Delete_GroupBy_Where_Select_First(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_2(bool async) + { + await base.Delete_GroupBy_Where_Select_First_2(async); + + AssertSql(); + } + public override async Task Update_where_hierarchy(bool async) { await base.Update_where_hierarchy(async); @@ -95,15 +123,17 @@ public override async Task Update_where_using_hierarchy(bool async) await base.Update_where_using_hierarchy(async); AssertExecuteUpdateSql( - @"UPDATE ""Countries"" AS c - SET ""Name"" = 'Monovia' +""" +UPDATE "Countries" AS c +SET "Name" = 'Monovia' WHERE ( SELECT count(*)::int - FROM ""Animals"" AS a - LEFT JOIN ""Birds"" AS b ON a.""Id"" = b.""Id"" - LEFT JOIN ""Eagle"" AS e ON a.""Id"" = e.""Id"" - LEFT JOIN ""Kiwi"" AS k ON a.""Id"" = k.""Id"" - WHERE a.""CountryId"" = 1 AND c.""Id"" = a.""CountryId"" AND a.""CountryId"" > 0) > 0"); + FROM "Animals" AS a + LEFT JOIN "Birds" AS b ON a."Id" = b."Id" + LEFT JOIN "Eagle" AS e ON a."Id" = e."Id" + LEFT JOIN "Kiwi" AS k ON a."Id" = k."Id" + WHERE a."CountryId" = 1 AND c."Id" = a."CountryId" AND a."CountryId" > 0) > 0 +"""); } public override async Task Update_where_using_hierarchy_derived(bool async) @@ -111,15 +141,17 @@ public override async Task Update_where_using_hierarchy_derived(bool async) await base.Update_where_using_hierarchy_derived(async); AssertExecuteUpdateSql( - @"UPDATE ""Countries"" AS c - SET ""Name"" = 'Monovia' +""" +UPDATE "Countries" AS c +SET "Name" = 'Monovia' WHERE ( SELECT count(*)::int - FROM ""Animals"" AS a - LEFT JOIN ""Birds"" AS b ON a.""Id"" = b.""Id"" - LEFT JOIN ""Eagle"" AS e ON a.""Id"" = e.""Id"" - LEFT JOIN ""Kiwi"" AS k ON a.""Id"" = k.""Id"" - WHERE a.""CountryId"" = 1 AND c.""Id"" = a.""CountryId"" AND (k.""Id"" IS NOT NULL) AND a.""CountryId"" > 0) > 0"); + FROM "Animals" AS a + LEFT JOIN "Birds" AS b ON a."Id" = b."Id" + LEFT JOIN "Eagle" AS e ON a."Id" = e."Id" + LEFT JOIN "Kiwi" AS k ON a."Id" = k."Id" + WHERE a."CountryId" = 1 AND c."Id" = a."CountryId" AND (k."Id" IS NOT NULL) AND a."CountryId" > 0) > 0 +"""); } public override async Task Update_where_keyless_entity_mapped_to_sql_query(bool async) diff --git a/test/EFCore.PG.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesNpgsqlTest.cs index 34955232..94e28f0a 100644 --- a/test/EFCore.PG.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesNpgsqlTest.cs @@ -4,10 +4,13 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.BulkUpdates; public class TPTInheritanceBulkUpdatesNpgsqlTest : TPTInheritanceBulkUpdatesTestBase { - public TPTInheritanceBulkUpdatesNpgsqlTest(TPTInheritanceBulkUpdatesNpgsqlFixture fixture) + public TPTInheritanceBulkUpdatesNpgsqlTest( + TPTInheritanceBulkUpdatesNpgsqlFixture fixture, + ITestOutputHelper testOutputHelper) : base(fixture) { ClearLog(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } public override async Task Delete_where_hierarchy(bool async) @@ -52,6 +55,27 @@ public override async Task Delete_where_hierarchy_subquery(bool async) AssertSql(); } + public override async Task Delete_GroupBy_Where_Select_First_3(bool async) + { + await base.Delete_GroupBy_Where_Select_First_3(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First(bool async) + { + await base.Delete_GroupBy_Where_Select_First(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_2(bool async) + { + await base.Delete_GroupBy_Where_Select_First_2(async); + + AssertSql(); + } + public override async Task Update_where_hierarchy(bool async) { await base.Update_where_hierarchy(async); @@ -78,15 +102,17 @@ public override async Task Update_where_using_hierarchy(bool async) await base.Update_where_using_hierarchy(async); AssertExecuteUpdateSql( - @"UPDATE ""Countries"" AS c - SET ""Name"" = 'Monovia' +""" +UPDATE "Countries" AS c +SET "Name" = 'Monovia' WHERE ( SELECT count(*)::int - FROM ""Animals"" AS a - LEFT JOIN ""Birds"" AS b ON a.""Id"" = b.""Id"" - LEFT JOIN ""Eagle"" AS e ON a.""Id"" = e.""Id"" - LEFT JOIN ""Kiwi"" AS k ON a.""Id"" = k.""Id"" - WHERE c.""Id"" = a.""CountryId"" AND a.""CountryId"" > 0) > 0"); + FROM "Animals" AS a + LEFT JOIN "Birds" AS b ON a."Id" = b."Id" + LEFT JOIN "Eagle" AS e ON a."Id" = e."Id" + LEFT JOIN "Kiwi" AS k ON a."Id" = k."Id" + WHERE c."Id" = a."CountryId" AND a."CountryId" > 0) > 0 +"""); } public override async Task Update_where_using_hierarchy_derived(bool async) @@ -94,15 +120,17 @@ public override async Task Update_where_using_hierarchy_derived(bool async) await base.Update_where_using_hierarchy_derived(async); AssertExecuteUpdateSql( - @"UPDATE ""Countries"" AS c - SET ""Name"" = 'Monovia' +""" +UPDATE "Countries" AS c +SET "Name" = 'Monovia' WHERE ( SELECT count(*)::int - FROM ""Animals"" AS a - LEFT JOIN ""Birds"" AS b ON a.""Id"" = b.""Id"" - LEFT JOIN ""Eagle"" AS e ON a.""Id"" = e.""Id"" - LEFT JOIN ""Kiwi"" AS k ON a.""Id"" = k.""Id"" - WHERE c.""Id"" = a.""CountryId"" AND (k.""Id"" IS NOT NULL) AND a.""CountryId"" > 0) > 0"); + FROM "Animals" AS a + LEFT JOIN "Birds" AS b ON a."Id" = b."Id" + LEFT JOIN "Eagle" AS e ON a."Id" = e."Id" + LEFT JOIN "Kiwi" AS k ON a."Id" = k."Id" + WHERE c."Id" = a."CountryId" AND (k."Id" IS NOT NULL) AND a."CountryId" > 0) > 0 +"""); } public override async Task Update_where_keyless_entity_mapped_to_sql_query(bool async) diff --git a/test/EFCore.PG.FunctionalTests/ManyToManyTrackingNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/ManyToManyTrackingNpgsqlTest.cs index ca0fc88d..95d4795d 100644 --- a/test/EFCore.PG.FunctionalTests/ManyToManyTrackingNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/ManyToManyTrackingNpgsqlTest.cs @@ -3,7 +3,8 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL; -public class ManyToManyTrackingNpgsqlTest : ManyToManyTrackingTestBase +public class ManyToManyTrackingNpgsqlTest : ManyToManyTrackingRelationalTestBase< + ManyToManyTrackingNpgsqlTest.ManyToManyTrackingNpgsqlFixture> { public ManyToManyTrackingNpgsqlTest(ManyToManyTrackingNpgsqlFixture fixture) : base(fixture) @@ -13,7 +14,7 @@ public ManyToManyTrackingNpgsqlTest(ManyToManyTrackingNpgsqlFixture fixture) protected override void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction) => facade.UseTransaction(transaction.GetDbTransaction()); - public class ManyToManyTrackingNpgsqlFixture : ManyToManyTrackingFixtureBase + public class ManyToManyTrackingNpgsqlFixture : ManyToManyTrackingRelationalFixture { protected override ITestStoreFactory TestStoreFactory => NpgsqlTestStoreFactory.Instance; diff --git a/test/EFCore.PG.FunctionalTests/Query/NorthwindGroupByQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/NorthwindGroupByQueryNpgsqlTest.cs index 2290c695..2e04c092 100644 --- a/test/EFCore.PG.FunctionalTests/Query/NorthwindGroupByQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/NorthwindGroupByQueryNpgsqlTest.cs @@ -2786,14 +2786,26 @@ public override async Task GroupBy_with_group_key_being_navigation(bool async) await base.GroupBy_with_group_key_being_navigation(async); AssertSql( - ); +""" +SELECT o0."OrderID", o0."CustomerID", o0."EmployeeID", o0."OrderDate", COALESCE(sum(o."OrderID"), 0)::int AS "Aggregate" +FROM "Order Details" AS o +INNER JOIN "Orders" AS o0 ON o."OrderID" = o0."OrderID" +GROUP BY o0."OrderID", o0."CustomerID", o0."EmployeeID", o0."OrderDate" +"""); } public override async Task GroupBy_with_group_key_being_nested_navigation(bool async) { await base.GroupBy_with_group_key_being_nested_navigation(async); - AssertSql(); + AssertSql( +""" +SELECT c."CustomerID", c."Address", c."City", c."CompanyName", c."ContactName", c."ContactTitle", c."Country", c."Fax", c."Phone", c."PostalCode", c."Region", COALESCE(sum(o."OrderID"), 0)::int AS "Aggregate" +FROM "Order Details" AS o +INNER JOIN "Orders" AS o0 ON o."OrderID" = o0."OrderID" +LEFT JOIN "Customers" AS c ON o0."CustomerID" = c."CustomerID" +GROUP BY c."CustomerID", c."Address", c."City", c."CompanyName", c."ContactName", c."ContactTitle", c."Country", c."Fax", c."Phone", c."PostalCode", c."Region" +"""); } public override async Task GroupBy_with_group_key_being_navigation_with_entity_key_projection(bool async) @@ -2801,7 +2813,12 @@ public override async Task GroupBy_with_group_key_being_navigation_with_entity_k await base.GroupBy_with_group_key_being_navigation_with_entity_key_projection(async); AssertSql( - ); +""" +SELECT o0."OrderID", o0."CustomerID", o0."EmployeeID", o0."OrderDate" +FROM "Order Details" AS o +INNER JOIN "Orders" AS o0 ON o."OrderID" = o0."OrderID" +GROUP BY o0."OrderID", o0."CustomerID", o0."EmployeeID", o0."OrderDate" +"""); } public override async Task GroupBy_with_group_key_being_navigation_with_complex_projection(bool async) @@ -3519,11 +3536,154 @@ public override async Task GroupBy_aggregate_SelectMany(bool async) AssertSql(); } - public override async Task GroupBy_as_final_operator(bool async) + public override async Task Final_GroupBy_property_entity(bool async) { - await base.GroupBy_as_final_operator(async); + await base.Final_GroupBy_property_entity(async); - AssertSql(); + AssertSql( +""" +SELECT c."City", c."CustomerID", c."Address", c."CompanyName", c."ContactName", c."ContactTitle", c."Country", c."Fax", c."Phone", c."PostalCode", c."Region" +FROM "Customers" AS c +ORDER BY c."City" NULLS FIRST +"""); + } + + public override async Task Final_GroupBy_property_anonymous_type(bool async) + { + await base.Final_GroupBy_property_anonymous_type(async); + + AssertSql( +""" +SELECT c."City", c."ContactName", c."ContactTitle" +FROM "Customers" AS c +ORDER BY c."City" NULLS FIRST +"""); + } + + public override async Task Final_GroupBy_multiple_properties_entity(bool async) + { + await base.Final_GroupBy_multiple_properties_entity(async); + + AssertSql( +""" +SELECT c."City", c."Region", c."CustomerID", c."Address", c."CompanyName", c."ContactName", c."ContactTitle", c."Country", c."Fax", c."Phone", c."PostalCode" +FROM "Customers" AS c +ORDER BY c."City" NULLS FIRST, c."Region" NULLS FIRST +"""); + } + + public override async Task Final_GroupBy_complex_key_entity(bool async) + { + await base.Final_GroupBy_complex_key_entity(async); + + AssertSql( +""" +SELECT t."City", t."Region", t."Constant", t."CustomerID", t."Address", t."CompanyName", t."ContactName", t."ContactTitle", t."Country", t."Fax", t."Phone", t."PostalCode" +FROM ( + SELECT c."CustomerID", c."Address", c."City", c."CompanyName", c."ContactName", c."ContactTitle", c."Country", c."Fax", c."Phone", c."PostalCode", c."Region", 1 AS "Constant" + FROM "Customers" AS c +) AS t +ORDER BY t."City" NULLS FIRST, t."Region" NULLS FIRST, t."Constant" NULLS FIRST +"""); + } + + public override async Task Final_GroupBy_nominal_type_entity(bool async) + { + await base.Final_GroupBy_nominal_type_entity(async); + + AssertSql( +""" +SELECT t."City", t."Constant", t."CustomerID", t."Address", t."CompanyName", t."ContactName", t."ContactTitle", t."Country", t."Fax", t."Phone", t."PostalCode", t."Region" +FROM ( + SELECT c."CustomerID", c."Address", c."City", c."CompanyName", c."ContactName", c."ContactTitle", c."Country", c."Fax", c."Phone", c."PostalCode", c."Region", 1 AS "Constant" + FROM "Customers" AS c +) AS t +ORDER BY t."City" NULLS FIRST, t."Constant" NULLS FIRST +"""); + } + + public override async Task Final_GroupBy_property_anonymous_type_element_selector(bool async) + { + await base.Final_GroupBy_property_anonymous_type_element_selector(async); + + AssertSql( +""" +SELECT c."City", c."ContactName", c."ContactTitle" +FROM "Customers" AS c +ORDER BY c."City" NULLS FIRST +"""); + } + + public override async Task Final_GroupBy_property_entity_Include_collection(bool async) + { + await base.Final_GroupBy_property_entity_Include_collection(async); + + AssertSql( +""" +SELECT c."City", c."CustomerID", c."Address", c."CompanyName", c."ContactName", c."ContactTitle", c."Country", c."Fax", c."Phone", c."PostalCode", c."Region", o."OrderID", o."CustomerID", o."EmployeeID", o."OrderDate" +FROM "Customers" AS c +LEFT JOIN "Orders" AS o ON c."CustomerID" = o."CustomerID" +WHERE c."Country" = 'USA' +ORDER BY c."City" NULLS FIRST, c."CustomerID" NULLS FIRST +"""); + } + + public override async Task Final_GroupBy_property_entity_projecting_collection(bool async) + { + await base.Final_GroupBy_property_entity_projecting_collection(async); + + AssertSql( +""" +SELECT c."City", c."CustomerID", o."OrderID", o."CustomerID", o."EmployeeID", o."OrderDate" +FROM "Customers" AS c +LEFT JOIN "Orders" AS o ON c."CustomerID" = o."CustomerID" +WHERE c."Country" = 'USA' +ORDER BY c."City" NULLS FIRST, c."CustomerID" NULLS FIRST +"""); + } + + public override async Task Final_GroupBy_property_entity_projecting_collection_composed(bool async) + { + await base.Final_GroupBy_property_entity_projecting_collection_composed(async); + + AssertSql( +""" +SELECT c."City", c."CustomerID", t."OrderID", t."CustomerID", t."EmployeeID", t."OrderDate" +FROM "Customers" AS c +LEFT JOIN ( + SELECT o."OrderID", o."CustomerID", o."EmployeeID", o."OrderDate" + FROM "Orders" AS o + WHERE o."OrderID" < 11000 +) AS t ON c."CustomerID" = t."CustomerID" +WHERE c."Country" = 'USA' +ORDER BY c."City" NULLS FIRST, c."CustomerID" NULLS FIRST +"""); + } + + public override async Task Final_GroupBy_property_entity_projecting_collection_and_single_result(bool async) + { + await base.Final_GroupBy_property_entity_projecting_collection_and_single_result(async); + + AssertSql( +""" +SELECT c."City", c."CustomerID", t."OrderID", t."CustomerID", t."EmployeeID", t."OrderDate", t0."OrderID", t0."CustomerID", t0."EmployeeID", t0."OrderDate" +FROM "Customers" AS c +LEFT JOIN ( + SELECT o."OrderID", o."CustomerID", o."EmployeeID", o."OrderDate" + FROM "Orders" AS o + WHERE o."OrderID" < 11000 +) AS t ON c."CustomerID" = t."CustomerID" +LEFT JOIN ( + SELECT t1."OrderID", t1."CustomerID", t1."EmployeeID", t1."OrderDate" + FROM ( + SELECT o0."OrderID", o0."CustomerID", o0."EmployeeID", o0."OrderDate", ROW_NUMBER() OVER(PARTITION BY o0."CustomerID" ORDER BY o0."OrderDate" DESC NULLS LAST) AS row + FROM "Orders" AS o0 + ) AS t1 + WHERE t1.row <= 1 +) AS t0 ON c."CustomerID" = t0."CustomerID" +WHERE c."Country" = 'USA' +ORDER BY c."City" NULLS FIRST, c."CustomerID" NULLS FIRST +"""); } public override async Task GroupBy_Where_with_grouping_result(bool async) diff --git a/test/EFCore.PG.FunctionalTests/Query/ToSqlQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/ToSqlQueryNpgsqlTest.cs new file mode 100644 index 00000000..635870f2 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/ToSqlQueryNpgsqlTest.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities; + +namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query; + +public class ToSqlQuerySqlServerTest : ToSqlQueryTestBase +{ + protected override ITestStoreFactory TestStoreFactory + => NpgsqlTestStoreFactory.Instance; + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + // Base test implementation does not properly use identifier delimiters in raw SQL and isn't usable on PostgreSQL + public override Task Entity_type_with_navigation_mapped_to_SqlQuery(bool async) + => Task.CompletedTask; + + private void AssertSql(params string[] expected) + => TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/TableSplittingNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/TableSplittingNpgsqlTest.cs index 7a34b96c..f7a6ffce 100644 --- a/test/EFCore.PG.FunctionalTests/TableSplittingNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/TableSplittingNpgsqlTest.cs @@ -18,13 +18,17 @@ public override async Task ExecuteUpdate_works_for_table_sharing(bool async) await base.ExecuteUpdate_works_for_table_sharing(async); AssertSql( - @"UPDATE ""Vehicles"" AS v - SET ""SeatingCapacity"" = 1", +""" +UPDATE "Vehicles" AS v +SET "SeatingCapacity" = 1 +""", // - @"SELECT NOT EXISTS ( +""" +SELECT NOT EXISTS ( SELECT 1 - FROM ""Vehicles"" AS v - WHERE v.""SeatingCapacity"" <> 1)"); + FROM "Vehicles" AS v + WHERE v."SeatingCapacity" <> 1) +"""); } protected override void OnModelCreating(ModelBuilder modelBuilder) diff --git a/test/EFCore.PG.FunctionalTests/UpdatesNpgsqlFixture.cs b/test/EFCore.PG.FunctionalTests/UpdatesNpgsqlFixture.cs deleted file mode 100644 index 260c734e..00000000 --- a/test/EFCore.PG.FunctionalTests/UpdatesNpgsqlFixture.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities; - -namespace Npgsql.EntityFrameworkCore.PostgreSQL; - -public class UpdatesNpgsqlFixture : UpdatesRelationalFixture -{ - protected override string StoreName { get; } = "PartialUpdateNpgsqlTest"; - protected override ITestStoreFactory TestStoreFactory => NpgsqlTestStoreFactory.Instance; -} \ No newline at end of file diff --git a/test/EFCore.PG.FunctionalTests/UpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/UpdatesNpgsqlTest.cs index 3f62f5d9..c781fe3c 100644 --- a/test/EFCore.PG.FunctionalTests/UpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/UpdatesNpgsqlTest.cs @@ -1,8 +1,9 @@ using Microsoft.EntityFrameworkCore.TestModels.UpdatesModel; +using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities; namespace Npgsql.EntityFrameworkCore.PostgreSQL; -public class UpdatesNpgsqlTest : UpdatesRelationalTestBase +public class UpdatesNpgsqlTest : UpdatesRelationalTestBase { // ReSharper disable once UnusedParameter.Local public UpdatesNpgsqlTest(UpdatesNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) @@ -50,4 +51,24 @@ public override void Identifiers_are_generated_correctly() "IX_LoginEntityTypeWithAnExtremelyLongAndOverlyConvolutedNameT~1", entityType2.GetIndexes().Single().GetDatabaseName()); } + + public class UpdatesNpgsqlFixture : UpdatesRelationalFixture + { + protected override ITestStoreFactory TestStoreFactory + => NpgsqlTestStoreFactory.Instance; + + protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) + { + base.OnModelCreating(modelBuilder, context); + + modelBuilder.HasPostgresExtension("uuid-ossp"); + + modelBuilder.Entity() + .Property(p => p.Id).HasDefaultValueSql("uuid_generate_v4()"); + + modelBuilder.Entity().HasIndex(p => new { p.Name, p.Price }).IsUnique().HasFilter(@"""Name"" IS NOT NULL"); + + modelBuilder.Entity().Property(r => r.Concurrency).HasColumnType("timestamp without time zone"); + } + } } -- GitLab