diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index afdac987c..2e21fac6a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ on: pull_request: env: - dotnet_sdk_version: '10.0.100-alpha.1.25059.31' + dotnet_sdk_version: '10.0.100-preview.2.25081.1' postgis_version: 3 DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 321dbab66..2277c425d 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -27,7 +27,7 @@ on: - cron: '30 22 * * 6' env: - dotnet_sdk_version: '10.0.100-alpha.1.25059.31' + dotnet_sdk_version: '10.0.100-preview.2.25081.1' jobs: analyze: diff --git a/Directory.Packages.props b/Directory.Packages.props index 339c7acf7..37aedaaf9 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,7 +1,7 @@ - 10.0.0-preview.1.25077.1 - 10.0.0-alpha.1.25073.13 + 10.0.0-preview.2.25103.6 + 10.0.0-preview.2.25102.2 9.0.2 diff --git a/global.json b/global.json index 4f8676115..d9e3bbb10 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "10.0.100-alpha.1.25059.31", + "version": "10.0.100-preview.2.25081.1", "rollForward": "latestMajor", "allowPrerelease": true } diff --git a/test/EFCore.PG.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesNpgsqlTest.cs index 50cb15039..30494430c 100644 --- a/test/EFCore.PG.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesNpgsqlTest.cs @@ -2,6 +2,8 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates; +#nullable disable + public class NorthwindBulkUpdatesNpgsqlTest( NorthwindBulkUpdatesNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) @@ -619,6 +621,30 @@ LIMIT 100 OFFSET 0 """); } + public override async Task Delete_with_RightJoin(bool async) + { + await base.Delete_with_RightJoin(async); + + AssertSql( + """ +@p0='100' +@p='0' + +DELETE FROM "Order Details" AS o +WHERE EXISTS ( + SELECT 1 + FROM "Order Details" AS o0 + RIGHT JOIN ( + SELECT o2."OrderID" + FROM "Orders" AS o2 + WHERE o2."OrderID" < 10300 + ORDER BY o2."OrderID" NULLS FIRST + LIMIT @p0 OFFSET @p + ) AS o1 ON o0."OrderID" = o1."OrderID" + WHERE o0."OrderID" < 10276 AND o0."OrderID" = o."OrderID" AND o0."ProductID" = o."ProductID") +"""); + } + public override async Task Update_Where_set_constant_TagWith(bool async) { await base.Update_Where_set_constant_TagWith(async); @@ -1287,6 +1313,41 @@ WHERE c."CustomerID" LIKE 'F%' """); } + public override async Task Update_with_RightJoin(bool async) + { + await AssertUpdate( + async, + ss => ss.Set().Where(o => o.OrderID < 10300) + .RightJoin( + ss.Set().Where(c => c.CustomerID.StartsWith("F")), + o => o.CustomerID, + c => c.CustomerID, + (o, c) => new { Order = o, Customers = c }), + e => e.Order, + s => s.SetProperty(t => t.Order.OrderDate, new DateTime(2020, 1, 1, 0, 0, 0)), + rowsAffectedCount: 2, + (b, a) => Assert.All(a, o => Assert.Equal(new DateTime(2020, 1, 1, 0, 0, 0), o.OrderDate))); + + AssertExecuteUpdateSql( + """ +@p='2020-01-01T00:00:00.0000000' (Nullable = true) + +UPDATE "Orders" AS o0 +SET "OrderDate" = @p +FROM ( + SELECT o."OrderID" + FROM "Orders" AS o + RIGHT JOIN ( + SELECT c."CustomerID" + FROM "Customers" AS c + WHERE c."CustomerID" LIKE 'F%' + ) AS c0 ON o."CustomerID" = c0."CustomerID" + WHERE o."OrderID" < 10300 +) AS s +WHERE o0."OrderID" = s."OrderID" +"""); + } + public override async Task Update_with_cross_join_set_constant(bool async) { await base.Update_with_cross_join_set_constant(async); diff --git a/test/EFCore.PG.FunctionalTests/Query/JsonQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/JsonQueryNpgsqlTest.cs index d530f1f35..47ca12b2c 100644 --- a/test/EFCore.PG.FunctionalTests/Query/JsonQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/JsonQueryNpgsqlTest.cs @@ -606,9 +606,9 @@ public override async Task Project_entity_with_single_owned(bool async) """); } - public override async Task Left_join_json_entities(bool async) + public override async Task LeftJoin_json_entities(bool async) { - await base.Left_join_json_entities(async); + await base.LeftJoin_json_entities(async); AssertSql( """ @@ -618,6 +618,18 @@ public override async Task Left_join_json_entities(bool async) """); } + public override async Task RightJoin_json_entities(bool async) + { + await base.RightJoin_json_entities(async); + + AssertSql( + """ +SELECT j."Id", j."EntityBasicId", j."Name", j."OwnedCollectionRoot", j."OwnedReferenceRoot", j0."Id", j0."Name", j0."OwnedCollection" +FROM "JsonEntitiesBasic" AS j +RIGHT JOIN "JsonEntitiesSingleOwned" AS j0 ON j."Id" = j0."Id" +"""); + } + public override async Task Left_join_json_entities_complex_projection(bool async) { await base.Left_join_json_entities_complex_projection(async); diff --git a/test/EFCore.PG.FunctionalTests/Query/NorthwindAggregateOperatorsQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/NorthwindAggregateOperatorsQueryNpgsqlTest.cs index 2e6d826e1..7970d1e5b 100644 --- a/test/EFCore.PG.FunctionalTests/Query/NorthwindAggregateOperatorsQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/NorthwindAggregateOperatorsQueryNpgsqlTest.cs @@ -67,36 +67,39 @@ public override async Task Contains_with_local_uint_array_closure(bool async) """); } - public override async Task Contains_with_local_nullable_uint_array_closure(bool async) - { - await base.Contains_with_local_nullable_uint_array_closure(async); - - // Note: PostgreSQL doesn't support uint, but value converters make this into bigint - - AssertSql( - """ -@ids={ '0', '1' } (DbType = Object) - -SELECT e."EmployeeID", e."City", e."Country", e."FirstName", e."ReportsTo", e."Title" -FROM "Employees" AS e -WHERE e."EmployeeID" = ANY (@ids) -""", - // - """ -@ids={ '0' } (DbType = Object) - -SELECT e."EmployeeID", e."City", e."Country", e."FirstName", e."ReportsTo", e."Title" -FROM "Employees" AS e -WHERE e."EmployeeID" = ANY (@ids) -"""); - } - - public override Task Contains_with_local_anonymous_type_array_closure(bool async) - // Aggregates. Issue #15937. - => AssertTranslationFailed(() => base.Contains_with_local_anonymous_type_array_closure(async)); - - public override Task Contains_with_local_tuple_array_closure(bool async) - => Assert.ThrowsAsync(() => base.Contains_with_local_tuple_array_closure(async: true)); +// TODO: The base implementations no longer compile since https://github.com/dotnet/runtime/pull/110197 (Contains overload added with +// optional parameter, not supported in expression trees). #35547 is tracking on the EF side. +// +// public override async Task Contains_with_local_nullable_uint_array_closure(bool async) +// { +// await base.Contains_with_local_nullable_uint_array_closure(async); +// +// // Note: PostgreSQL doesn't support uint, but value converters make this into bigint +// +// AssertSql( +// """ +// @ids={ '0', '1' } (DbType = Object) +// +// SELECT e."EmployeeID", e."City", e."Country", e."FirstName", e."ReportsTo", e."Title" +// FROM "Employees" AS e +// WHERE e."EmployeeID" = ANY (@ids) +// """, +// // +// """ +// @ids={ '0' } (DbType = Object) +// +// SELECT e."EmployeeID", e."City", e."Country", e."FirstName", e."ReportsTo", e."Title" +// FROM "Employees" AS e +// WHERE e."EmployeeID" = ANY (@ids) +// """); +// } +// +// public override Task Contains_with_local_anonymous_type_array_closure(bool async) +// // Aggregates. Issue #15937. +// => AssertTranslationFailed(() => base.Contains_with_local_anonymous_type_array_closure(async)); +// +// public override Task Contains_with_local_tuple_array_closure(bool async) +// => Assert.ThrowsAsync(() => base.Contains_with_local_tuple_array_closure(async: true)); public override async Task Contains_with_local_enumerable_inline(bool async) { diff --git a/test/EFCore.PG.FunctionalTests/Query/PrimitiveCollectionsQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/PrimitiveCollectionsQueryNpgsqlTest.cs index a976e722b..bc5b94d72 100644 --- a/test/EFCore.PG.FunctionalTests/Query/PrimitiveCollectionsQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/PrimitiveCollectionsQueryNpgsqlTest.cs @@ -22,29 +22,32 @@ public override async Task Inline_collection_of_ints_Contains(bool async) """); } - public override async Task Inline_collection_of_nullable_ints_Contains(bool async) - { - await base.Inline_collection_of_nullable_ints_Contains(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."NullableWrappedId", p."NullableWrappedIdWithNullableComparer", p."String", p."Strings", p."WrappedId" -FROM "PrimitiveCollectionsEntity" AS p -WHERE p."NullableInt" IN (10, 999) -"""); - } - - public override async Task Inline_collection_of_nullable_ints_Contains_null(bool async) - { - await base.Inline_collection_of_nullable_ints_Contains_null(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."NullableWrappedId", p."NullableWrappedIdWithNullableComparer", p."String", p."Strings", p."WrappedId" -FROM "PrimitiveCollectionsEntity" AS p -WHERE p."NullableInt" IS NULL OR p."NullableInt" = 999 -"""); - } +// TODO: The base implementations no longer compile since https://github.com/dotnet/runtime/pull/110197 (Contains overload added with +// optional parameter, not supported in expression trees). #35547 is tracking on the EF side. +// +// public override async Task Inline_collection_of_nullable_ints_Contains(bool async) +// { +// await base.Inline_collection_of_nullable_ints_Contains(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."NullableWrappedId", p."NullableWrappedIdWithNullableComparer", p."String", p."Strings", p."WrappedId" +// FROM "PrimitiveCollectionsEntity" AS p +// WHERE p."NullableInt" IN (10, 999) +// """); +// } +// +// public override async Task Inline_collection_of_nullable_ints_Contains_null(bool async) +// { +// await base.Inline_collection_of_nullable_ints_Contains_null(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."NullableWrappedId", p."NullableWrappedIdWithNullableComparer", p."String", p."Strings", p."WrappedId" +// FROM "PrimitiveCollectionsEntity" AS p +// WHERE p."NullableInt" IS NULL OR p."NullableInt" = 999 +// """); +// } public override async Task Inline_collection_Count_with_zero_values(bool async) { @@ -572,49 +575,52 @@ WHERE NOT (p."NullableInt" = ANY (@ints) AND p."NullableInt" = ANY (@ints) IS NO """); } - public override async Task Parameter_collection_of_nullable_ints_Contains_int(bool async) - { - await base.Parameter_collection_of_nullable_ints_Contains_int(async); - - AssertSql( - """ -@nullableInts={ '10', '999' } (DbType = Object) - -SELECT p."Id", p."Bool", p."Bools", p."DateTime", p."DateTimes", p."Enum", p."Enums", p."Int", p."Ints", p."NullableInt", p."NullableInts", p."NullableString", p."NullableStrings", p."NullableWrappedId", p."NullableWrappedIdWithNullableComparer", p."String", p."Strings", p."WrappedId" -FROM "PrimitiveCollectionsEntity" AS p -WHERE p."Int" = ANY (@nullableInts) -""", - // - """ -@nullableInts={ '10', '999' } (DbType = Object) - -SELECT p."Id", p."Bool", p."Bools", p."DateTime", p."DateTimes", p."Enum", p."Enums", p."Int", p."Ints", p."NullableInt", p."NullableInts", p."NullableString", p."NullableStrings", p."NullableWrappedId", p."NullableWrappedIdWithNullableComparer", p."String", p."Strings", p."WrappedId" -FROM "PrimitiveCollectionsEntity" AS p -WHERE NOT (p."Int" = ANY (@nullableInts) AND p."Int" = ANY (@nullableInts) IS NOT NULL) -"""); - } - - public override async Task Parameter_collection_of_nullable_ints_Contains_nullable_int(bool async) - { - await base.Parameter_collection_of_nullable_ints_Contains_nullable_int(async); - - AssertSql( - """ -@nullableInts={ NULL, '999' } (DbType = Object) - -SELECT p."Id", p."Bool", p."Bools", p."DateTime", p."DateTimes", p."Enum", p."Enums", p."Int", p."Ints", p."NullableInt", p."NullableInts", p."NullableString", p."NullableStrings", p."NullableWrappedId", p."NullableWrappedIdWithNullableComparer", p."String", p."Strings", p."WrappedId" -FROM "PrimitiveCollectionsEntity" AS p -WHERE p."NullableInt" = ANY (@nullableInts) OR (p."NullableInt" IS NULL AND array_position(@nullableInts, NULL) IS NOT NULL) -""", - // - """ -@nullableInts={ NULL, '999' } (DbType = Object) - -SELECT p."Id", p."Bool", p."Bools", p."DateTime", p."DateTimes", p."Enum", p."Enums", p."Int", p."Ints", p."NullableInt", p."NullableInts", p."NullableString", p."NullableStrings", p."NullableWrappedId", p."NullableWrappedIdWithNullableComparer", p."String", p."Strings", p."WrappedId" -FROM "PrimitiveCollectionsEntity" AS p -WHERE NOT (p."NullableInt" = ANY (@nullableInts) AND p."NullableInt" = ANY (@nullableInts) IS NOT NULL) AND (p."NullableInt" IS NOT NULL OR array_position(@nullableInts, NULL) IS NULL) -"""); - } +// TODO: The base implementations no longer compile since https://github.com/dotnet/runtime/pull/110197 (Contains overload added with +// optional parameter, not supported in expression trees). #35547 is tracking on the EF side. +// +// public override async Task Parameter_collection_of_nullable_ints_Contains_int(bool async) +// { +// await base.Parameter_collection_of_nullable_ints_Contains_int(async); +// +// AssertSql( +// """ +// @nullableInts={ '10', '999' } (DbType = Object) +// +// SELECT p."Id", p."Bool", p."Bools", p."DateTime", p."DateTimes", p."Enum", p."Enums", p."Int", p."Ints", p."NullableInt", p."NullableInts", p."NullableString", p."NullableStrings", p."NullableWrappedId", p."NullableWrappedIdWithNullableComparer", p."String", p."Strings", p."WrappedId" +// FROM "PrimitiveCollectionsEntity" AS p +// WHERE p."Int" = ANY (@nullableInts) +// """, +// // +// """ +// @nullableInts={ '10', '999' } (DbType = Object) +// +// SELECT p."Id", p."Bool", p."Bools", p."DateTime", p."DateTimes", p."Enum", p."Enums", p."Int", p."Ints", p."NullableInt", p."NullableInts", p."NullableString", p."NullableStrings", p."NullableWrappedId", p."NullableWrappedIdWithNullableComparer", p."String", p."Strings", p."WrappedId" +// FROM "PrimitiveCollectionsEntity" AS p +// WHERE NOT (p."Int" = ANY (@nullableInts) AND p."Int" = ANY (@nullableInts) IS NOT NULL) +// """); +// } +// +// public override async Task Parameter_collection_of_nullable_ints_Contains_nullable_int(bool async) +// { +// await base.Parameter_collection_of_nullable_ints_Contains_nullable_int(async); +// +// AssertSql( +// """ +// @nullableInts={ NULL, '999' } (DbType = Object) +// +// SELECT p."Id", p."Bool", p."Bools", p."DateTime", p."DateTimes", p."Enum", p."Enums", p."Int", p."Ints", p."NullableInt", p."NullableInts", p."NullableString", p."NullableStrings", p."NullableWrappedId", p."NullableWrappedIdWithNullableComparer", p."String", p."Strings", p."WrappedId" +// FROM "PrimitiveCollectionsEntity" AS p +// WHERE p."NullableInt" = ANY (@nullableInts) OR (p."NullableInt" IS NULL AND array_position(@nullableInts, NULL) IS NOT NULL) +// """, +// // +// """ +// @nullableInts={ NULL, '999' } (DbType = Object) +// +// SELECT p."Id", p."Bool", p."Bools", p."DateTime", p."DateTimes", p."Enum", p."Enums", p."Int", p."Ints", p."NullableInt", p."NullableInts", p."NullableString", p."NullableStrings", p."NullableWrappedId", p."NullableWrappedIdWithNullableComparer", p."String", p."Strings", p."WrappedId" +// FROM "PrimitiveCollectionsEntity" AS p +// WHERE NOT (p."NullableInt" = ANY (@nullableInts) AND p."NullableInt" = ANY (@nullableInts) IS NOT NULL) AND (p."NullableInt" IS NOT NULL OR array_position(@nullableInts, NULL) IS NULL) +// """); +// } public override async Task Parameter_collection_of_structs_Contains_struct(bool async) { @@ -754,19 +760,22 @@ public override async Task Parameter_collection_of_bools_Contains(bool async) """); } - public override async Task Parameter_collection_of_enums_Contains(bool async) - { - await base.Parameter_collection_of_enums_Contains(async); - - AssertSql( - """ -@enums={ '0', '3' } (DbType = Object) - -SELECT p."Id", p."Bool", p."Bools", p."DateTime", p."DateTimes", p."Enum", p."Enums", p."Int", p."Ints", p."NullableInt", p."NullableInts", p."NullableString", p."NullableStrings", p."NullableWrappedId", p."NullableWrappedIdWithNullableComparer", p."String", p."Strings", p."WrappedId" -FROM "PrimitiveCollectionsEntity" AS p -WHERE p."Enum" = ANY (@enums) -"""); - } +// TODO: The base implementations no longer compile since https://github.com/dotnet/runtime/pull/110197 (Contains overload added with +// optional parameter, not supported in expression trees). #35547 is tracking on the EF side. +// +// public override async Task Parameter_collection_of_enums_Contains(bool async) +// { +// await base.Parameter_collection_of_enums_Contains(async); +// +// AssertSql( +// """ +// @enums={ '0', '3' } (DbType = Object) +// +// SELECT p."Id", p."Bool", p."Bools", p."DateTime", p."DateTimes", p."Enum", p."Enums", p."Int", p."Ints", p."NullableInt", p."NullableInts", p."NullableString", p."NullableStrings", p."NullableWrappedId", p."NullableWrappedIdWithNullableComparer", p."String", p."Strings", p."WrappedId" +// FROM "PrimitiveCollectionsEntity" AS p +// WHERE p."Enum" = ANY (@enums) +// """); +// } public override async Task Parameter_collection_null_Contains(bool async) { @@ -860,29 +869,32 @@ public override async Task Column_collection_of_ints_Contains(bool async) """); } - public override async Task Column_collection_of_nullable_ints_Contains(bool async) - { - await base.Column_collection_of_nullable_ints_Contains(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."NullableWrappedId", p."NullableWrappedIdWithNullableComparer", p."String", p."Strings", p."WrappedId" -FROM "PrimitiveCollectionsEntity" AS p -WHERE p."NullableInts" @> ARRAY[10]::integer[] -"""); - } - - public override async Task Column_collection_of_nullable_ints_Contains_null(bool async) - { - await base.Column_collection_of_nullable_ints_Contains_null(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."NullableWrappedId", p."NullableWrappedIdWithNullableComparer", p."String", p."Strings", p."WrappedId" -FROM "PrimitiveCollectionsEntity" AS p -WHERE array_position(p."NullableInts", NULL) IS NOT NULL -"""); - } +// TODO: The base implementations no longer compile since https://github.com/dotnet/runtime/pull/110197 (Contains overload added with +// optional parameter, not supported in expression trees). #35547 is tracking on the EF side. +// +// public override async Task Column_collection_of_nullable_ints_Contains(bool async) +// { +// await base.Column_collection_of_nullable_ints_Contains(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."NullableWrappedId", p."NullableWrappedIdWithNullableComparer", p."String", p."Strings", p."WrappedId" +// FROM "PrimitiveCollectionsEntity" AS p +// WHERE p."NullableInts" @> ARRAY[10]::integer[] +// """); +// } +// +// public override async Task Column_collection_of_nullable_ints_Contains_null(bool async) +// { +// await base.Column_collection_of_nullable_ints_Contains_null(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."NullableWrappedId", p."NullableWrappedIdWithNullableComparer", p."String", p."Strings", p."WrappedId" +// FROM "PrimitiveCollectionsEntity" AS p +// WHERE array_position(p."NullableInts", NULL) IS NOT NULL +// """); +// } public override async Task Column_collection_of_strings_contains_null(bool async) {