diff --git a/Directory.Build.props b/Directory.Build.props index 9dc6ee6615..8ca72be70d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -6,7 +6,7 @@ enable true latest - NU5105 + $(NoWarn);NU5105 true $(MSBuildThisFileDirectory)Npgsql.snk true diff --git a/Directory.Packages.props b/Directory.Packages.props index 19764965fd..f464b1457b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,7 +1,7 @@ - 10.0.0-preview.2.25163.8 - 10.0.0-preview.2.25163.2 + 10.0.0-preview.3.25171.6 + 10.0.0-preview.3.25171.5 9.0.3 @@ -26,7 +26,7 @@ - + diff --git a/src/EFCore.PG/Metadata/Conventions/NpgsqlConventionSetBuilder.cs b/src/EFCore.PG/Metadata/Conventions/NpgsqlConventionSetBuilder.cs index 1e1d22ae9b..12065162ef 100644 --- a/src/EFCore.PG/Metadata/Conventions/NpgsqlConventionSetBuilder.cs +++ b/src/EFCore.PG/Metadata/Conventions/NpgsqlConventionSetBuilder.cs @@ -51,8 +51,6 @@ public override ConventionSet CreateConventionSet() conventionSet.ModelInitializedConventions.Add( new RelationalMaxIdentifierLengthConvention(63, Dependencies, RelationalDependencies)); - conventionSet.PropertyAddedConventions.Add(new NpgsqlJsonElementHackConvention()); - ValueGenerationConvention valueGenerationConvention = new NpgsqlValueGenerationConvention(Dependencies, RelationalDependencies); ReplaceConvention(conventionSet.EntityTypeBaseTypeChangedConventions, valueGenerationConvention); diff --git a/src/EFCore.PG/Metadata/Conventions/NpgsqlJsonElementHackConvention.cs b/src/EFCore.PG/Metadata/Conventions/NpgsqlJsonElementHackConvention.cs deleted file mode 100644 index dad88135fc..0000000000 --- a/src/EFCore.PG/Metadata/Conventions/NpgsqlJsonElementHackConvention.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Text.Json; -using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping; - -namespace Npgsql.EntityFrameworkCore.PostgreSQL.Metadata.Conventions; - -/// -/// This convention is a hack around https://github.com/dotnet/efcore/issues/32192. To support the EF owned entity JSON support, EF -/// requires -/// that a lookup of the CLR type return the provider's special . But Npgsql has -/// its own JSON DOM support, where actually mapping is allowed as a weakly-typed mapping strategy. The two -/// JSON type mappings are incompatible notably because EF's is expected to return UTF8 byte data which is -/// then parsed via (and not a string). So for properties actually typed as , we -/// hack here and set the type mapping rather than going through the regular type mapping process. -/// -public class NpgsqlJsonElementHackConvention : IPropertyAddedConvention -{ - private NpgsqlJsonTypeMapping? _jsonTypeMapping; - - /// - public void ProcessPropertyAdded(IConventionPropertyBuilder propertyBuilder, IConventionContext context) - { - var property = propertyBuilder.Metadata; - - if (property.ClrType.UnwrapNullableType() == typeof(JsonElement) && property.GetColumnType() is null) - { - property.SetTypeMapping(_jsonTypeMapping ??= new NpgsqlJsonTypeMapping("jsonb", typeof(JsonElement))); - } - } -} diff --git a/src/EFCore.PG/Storage/Internal/NpgsqlTypeMappingSource.cs b/src/EFCore.PG/Storage/Internal/NpgsqlTypeMappingSource.cs index e5719e76e4..30328fe9c3 100644 --- a/src/EFCore.PG/Storage/Internal/NpgsqlTypeMappingSource.cs +++ b/src/EFCore.PG/Storage/Internal/NpgsqlTypeMappingSource.cs @@ -314,8 +314,8 @@ public NpgsqlTypeMappingSource( { typeof(BigInteger), _bigInteger }, { typeof(string), _text }, { typeof(JsonDocument), _jsonbDocument }, - // { typeof(JsonElement), _jsonbElement }, - { typeof(JsonElement), _jsonbOwned }, + { typeof(JsonElement), _jsonbElement }, + { typeof(JsonTypePlaceholder), _jsonbOwned }, { typeof(char), _singleChar }, { typeof(DateTime), LegacyTimestampBehavior ? _timestamp : _timestamptz }, { typeof(DateOnly), _dateDateOnly }, diff --git a/test/EFCore.PG.FunctionalTests/LazyLoadProxyNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/LazyLoadProxyNpgsqlTest.cs index 39f760f90b..affdd7bf3f 100644 --- a/test/EFCore.PG.FunctionalTests/LazyLoadProxyNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/LazyLoadProxyNpgsqlTest.cs @@ -1,7 +1,7 @@ namespace Microsoft.EntityFrameworkCore; // ReSharper disable once UnusedMember.Global -public class LazyLoadProxyNpgsqlTest : LazyLoadProxyTestBase +public class LazyLoadProxyNpgsqlTest : LazyLoadProxyRelationalTestBase { public LazyLoadProxyNpgsqlTest(LoadNpgsqlFixture fixture) : base(fixture) @@ -1491,7 +1491,7 @@ protected override string SerializedBlogs1 #endregion Expected JSON override - public class LoadNpgsqlFixture : LoadFixtureBase + public class LoadNpgsqlFixture : LoadRelationalFixtureBase { public TestSqlLoggerFactory TestSqlLoggerFactory => (TestSqlLoggerFactory)ServiceProvider.GetRequiredService(); diff --git a/test/EFCore.PG.FunctionalTests/Query/AdHocJsonQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/AdHocJsonQueryNpgsqlTest.cs index 5b2acfc38d..47004e2f88 100644 --- a/test/EFCore.PG.FunctionalTests/Query/AdHocJsonQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/AdHocJsonQueryNpgsqlTest.cs @@ -2,7 +2,7 @@ namespace Microsoft.EntityFrameworkCore.Query; #nullable disable -public class AdHocJsonQueryNpgsqlTest : AdHocJsonQueryTestBase +public class AdHocJsonQueryNpgsqlTest : AdHocJsonQueryRelationalTestBase { protected override ITestStoreFactory TestStoreFactory => NpgsqlTestStoreFactory.Instance; @@ -296,7 +296,7 @@ public class TypesJsonEntity public TimeSpan Interval { get; set; } } - #region 34960 + #region Problematc tests (Unspecified DateTime) // These tests use a model with a non-UTC DateTime, which isn't supported in PG's timestamp with time zone @@ -309,12 +309,6 @@ public override Task Try_project_collection_but_JSON_is_entity() public override Task Try_project_reference_but_JSON_is_collection() => Assert.ThrowsAsync(() => base.Try_project_reference_but_JSON_is_collection()); - #endregion 34960 - - #region 34293 - - // These tests use a model with a non-UTC DateTime, which isn't supported in PG's timestamp with time zone - public override Task Project_entity_with_optional_json_entity_owned_by_required_json() => Assert.ThrowsAsync(() => base.Project_entity_with_optional_json_entity_owned_by_required_json()); @@ -324,7 +318,37 @@ public override Task Project_required_json_entity() public override Task Project_optional_json_entity_owned_by_required_json_entity() => Assert.ThrowsAsync(() => base.Project_optional_json_entity_owned_by_required_json_entity()); - #endregion 34293 + public override Task Project_missing_required_scalar(bool async) + => Assert.ThrowsAsync(() => base.Project_missing_required_scalar(async)); + + public override Task Project_nested_json_entity_with_missing_scalars(bool async) + => Assert.ThrowsAsync(() => base.Project_nested_json_entity_with_missing_scalars(async)); + + public override Task Project_null_required_scalar(bool async) + => Assert.ThrowsAsync(() => base.Project_null_required_scalar(async)); + + public override Task Project_root_entity_with_missing_required_navigation(bool async) + => Assert.ThrowsAsync(() => base.Project_root_entity_with_missing_required_navigation(async)); + + public override Task Project_root_entity_with_null_required_navigation(bool async) + => Assert.ThrowsAsync(() => base.Project_root_entity_with_null_required_navigation(async)); + + public override Task Project_root_with_missing_scalars(bool async) + => Assert.ThrowsAsync(() => base.Project_root_with_missing_scalars(async)); + + public override Task Project_top_level_json_entity_with_missing_scalars(bool async) + => Assert.ThrowsAsync(() => base.Project_top_level_json_entity_with_missing_scalars(async)); + + public override Task Project_missing_required_navigation(bool async) + => Task.CompletedTask; // Different exception expected in the base implementation + + public override Task Project_null_required_navigation(bool async) + => Task.CompletedTask; // Different exception expected in the base implementation + + public override Task Project_top_level_entity_with_null_value_required_scalars(bool async) + => Task.CompletedTask; // Different exception expected in the base implementation + + #endregion Problematc tests (Unspecified DateTime) protected void AssertSql(params string[] expected) => TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.PG.FunctionalTests/Query/AdHocMiscellaneousQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/AdHocMiscellaneousQueryNpgsqlTest.cs index 10c82e7eeb..6725b8fa8d 100644 --- a/test/EFCore.PG.FunctionalTests/Query/AdHocMiscellaneousQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/AdHocMiscellaneousQueryNpgsqlTest.cs @@ -1,3 +1,5 @@ +using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; + namespace Microsoft.EntityFrameworkCore.Query; public class AdHocMiscellaneousQueryNpgsqlTest : AdHocMiscellaneousQueryRelationalTestBase @@ -5,6 +7,17 @@ public class AdHocMiscellaneousQueryNpgsqlTest : AdHocMiscellaneousQueryRelation protected override ITestStoreFactory TestStoreFactory => NpgsqlTestStoreFactory.Instance; + protected override DbContextOptionsBuilder SetTranslateParameterizedCollectionsToConstants(DbContextOptionsBuilder optionsBuilder) + { + new NpgsqlDbContextOptionsBuilder(optionsBuilder).TranslateParameterizedCollectionsToConstants(); + + return optionsBuilder; + } + + // Unlike the other providers, EFCore.PG does actually support mapping JsonElement + public override Task Mapping_JsonElement_property_throws_a_meaningful_exception() + => Task.CompletedTask; + protected override Task Seed2951(Context2951 context) => context.Database.ExecuteSqlRawAsync( """ diff --git a/test/EFCore.PG.FunctionalTests/Query/CompatibilityQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/CompatibilityQueryNpgsqlTest.cs index 236448fff4..9acb50692e 100644 --- a/test/EFCore.PG.FunctionalTests/Query/CompatibilityQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/CompatibilityQueryNpgsqlTest.cs @@ -25,7 +25,7 @@ public async Task Array_contains_is_not_parameterized_with_array_on_redshift() """ SELECT t."Id", t."SomeInt" FROM "TestEntities" AS t -WHERE t."SomeInt" IN (8, 9) +WHERE t."SomeInt" IN (?, ?) LIMIT 2 """); } diff --git a/test/EFCore.PG.FunctionalTests/Query/JsonQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/JsonQueryNpgsqlTest.cs index 47ca12b2cf..5d2e42a4ba 100644 --- a/test/EFCore.PG.FunctionalTests/Query/JsonQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/JsonQueryNpgsqlTest.cs @@ -11,28 +11,6 @@ public JsonQueryNpgsqlTest(JsonQueryNpgsqlFixture fixture, ITestOutputHelper tes Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } - public override async Task Basic_json_projection_owner_entity(bool async) - { - await base.Basic_json_projection_owner_entity(async); - - AssertSql( - """ -SELECT j."Id", j."EntityBasicId", j."Name", j."OwnedCollectionRoot", j."OwnedReferenceRoot" -FROM "JsonEntitiesBasic" AS j -"""); - } - - public override async Task Basic_json_projection_owner_entity_NoTracking(bool async) - { - await base.Basic_json_projection_owner_entity_NoTracking(async); - - AssertSql( - """ -SELECT j."Id", j."EntityBasicId", j."Name", j."OwnedCollectionRoot", j."OwnedReferenceRoot" -FROM "JsonEntitiesBasic" AS j -"""); - } - public override async Task Basic_json_projection_owner_entity_NoTrackingWithIdentityResolution(bool async) { await base.Basic_json_projection_owner_entity_NoTrackingWithIdentityResolution(async); @@ -44,28 +22,6 @@ public override async Task Basic_json_projection_owner_entity_NoTrackingWithIden """); } - public override async Task Basic_json_projection_owner_entity_duplicated(bool async) - { - await base.Basic_json_projection_owner_entity_duplicated(async); - - AssertSql( - """ -SELECT j."Id", j."EntityBasicId", j."Name", j."OwnedCollectionRoot", j."OwnedReferenceRoot", j."OwnedCollectionRoot", j."OwnedReferenceRoot" -FROM "JsonEntitiesBasic" AS j -"""); - } - - public override async Task Basic_json_projection_owner_entity_duplicated_NoTracking(bool async) - { - await base.Basic_json_projection_owner_entity_duplicated_NoTracking(async); - - AssertSql( - """ -SELECT j."Id", j."Name", j."OwnedCollection", j."OwnedCollection" -FROM "JsonEntitiesSingleOwned" AS j -"""); - } - public override async Task Basic_json_projection_owner_entity_duplicated_NoTrackingWithIdentityResolution(bool async) { await base.Basic_json_projection_owner_entity_duplicated_NoTrackingWithIdentityResolution(async); @@ -77,28 +33,6 @@ public override async Task Basic_json_projection_owner_entity_duplicated_NoTrack """); } - public override async Task Basic_json_projection_owner_entity_twice(bool async) - { - await base.Basic_json_projection_owner_entity_twice(async); - - AssertSql( - """ -SELECT j."Id", j."EntityBasicId", j."Name", j."OwnedCollectionRoot", j."OwnedReferenceRoot", j."OwnedCollectionRoot", j."OwnedReferenceRoot" -FROM "JsonEntitiesBasic" AS j -"""); - } - - public override async Task Basic_json_projection_owner_entity_twice_NoTracking(bool async) - { - await base.Basic_json_projection_owner_entity_twice_NoTracking(async); - - AssertSql( - """ -SELECT j."Id", j."EntityBasicId", j."Name", j."OwnedCollectionRoot", j."OwnedReferenceRoot", j."OwnedCollectionRoot", j."OwnedReferenceRoot" -FROM "JsonEntitiesBasic" AS j -"""); - } - public override async Task Basic_json_projection_owner_entity_twice_NoTrackingWithIdentityResolution(bool async) { await base.Basic_json_projection_owner_entity_twice_NoTrackingWithIdentityResolution(async); @@ -134,17 +68,6 @@ public override async Task Project_json_entity_in_tracking_query_fails_even_when ); } - public override async Task Basic_json_projection_owned_reference_root(bool async) - { - await base.Basic_json_projection_owned_reference_root(async); - - AssertSql( - """ -SELECT j."OwnedReferenceRoot", j."Id" -FROM "JsonEntitiesBasic" AS j -"""); - } - public override async Task Basic_json_projection_owned_reference_root_NoTrackingWithIdentityResolution(bool async) { await base.Basic_json_projection_owned_reference_root_NoTrackingWithIdentityResolution(async); @@ -156,18 +79,6 @@ public override async Task Basic_json_projection_owned_reference_root_NoTracking """); } - public override async Task Basic_json_projection_owned_reference_duplicated2(bool async) - { - await base.Basic_json_projection_owned_reference_duplicated2(async); - - AssertSql( - """ -SELECT j."OwnedReferenceRoot", j."Id", j."OwnedReferenceRoot" #> '{OwnedReferenceBranch,OwnedReferenceLeaf}', j."OwnedReferenceRoot", j."OwnedReferenceRoot" #> '{OwnedReferenceBranch,OwnedReferenceLeaf}' -FROM "JsonEntitiesBasic" AS j -ORDER BY j."Id" NULLS FIRST -"""); - } - public override async Task Basic_json_projection_owned_reference_duplicated2_NoTrackingWithIdentityResolution(bool async) { await base.Basic_json_projection_owned_reference_duplicated2_NoTrackingWithIdentityResolution(async); @@ -180,18 +91,6 @@ ORDER BY j."Id" NULLS FIRST """); } - public override async Task Basic_json_projection_owned_reference_duplicated(bool async) - { - await base.Basic_json_projection_owned_reference_duplicated(async); - - AssertSql( - """ -SELECT j."OwnedReferenceRoot", j."Id", j."OwnedReferenceRoot" -> 'OwnedReferenceBranch', j."OwnedReferenceRoot", j."OwnedReferenceRoot" -> 'OwnedReferenceBranch' -FROM "JsonEntitiesBasic" AS j -ORDER BY j."Id" NULLS FIRST -"""); - } - public override async Task Basic_json_projection_owned_reference_duplicated_NoTrackingWithIdentityResolution(bool async) { await base.Basic_json_projection_owned_reference_duplicated_NoTrackingWithIdentityResolution(async); @@ -204,17 +103,6 @@ ORDER BY j."Id" NULLS FIRST """); } - public override async Task Basic_json_projection_owned_collection_root(bool async) - { - await base.Basic_json_projection_owned_collection_root(async); - - AssertSql( - """ -SELECT j."OwnedCollectionRoot", j."Id" -FROM "JsonEntitiesBasic" AS j -"""); - } - public override async Task Basic_json_projection_owned_collection_root_NoTrackingWithIdentityResolution(bool async) { await base.Basic_json_projection_owned_collection_root_NoTrackingWithIdentityResolution(async); @@ -226,17 +114,6 @@ public override async Task Basic_json_projection_owned_collection_root_NoTrackin """); } - public override async Task Basic_json_projection_owned_reference_branch(bool async) - { - await base.Basic_json_projection_owned_reference_branch(async); - - AssertSql( - """ -SELECT j."OwnedReferenceRoot" -> 'OwnedReferenceBranch', j."Id" -FROM "JsonEntitiesBasic" AS j -"""); - } - public override async Task Basic_json_projection_owned_reference_branch_NoTrackingWithIdentityResolution(bool async) { await base.Basic_json_projection_owned_reference_branch_NoTrackingWithIdentityResolution(async); @@ -248,17 +125,6 @@ public override async Task Basic_json_projection_owned_reference_branch_NoTracki """); } - public override async Task Basic_json_projection_owned_collection_branch(bool async) - { - await base.Basic_json_projection_owned_collection_branch(async); - - AssertSql( - """ -SELECT j."OwnedReferenceRoot" -> 'OwnedCollectionBranch', j."Id" -FROM "JsonEntitiesBasic" AS j -"""); - } - public override async Task Basic_json_projection_owned_collection_branch_NoTrackingWithIdentityResolution(bool async) { await base.Basic_json_projection_owned_collection_branch_NoTrackingWithIdentityResolution(async); @@ -337,28 +203,6 @@ public override async Task Json_projection_enum_with_custom_conversion(bool asyn """); } - public override async Task Json_projection_with_deduplication(bool async) - { - await base.Json_projection_with_deduplication(async); - - AssertSql( - """ -SELECT j."Id", j."OwnedReferenceRoot" -> 'OwnedReferenceBranch', j."OwnedReferenceRoot" #> '{OwnedReferenceBranch,OwnedReferenceLeaf}', j."OwnedReferenceRoot" #> '{OwnedReferenceBranch,OwnedCollectionLeaf}', j."OwnedReferenceRoot" -> 'OwnedCollectionBranch', j."OwnedReferenceRoot" #>> '{OwnedReferenceBranch,OwnedReferenceLeaf,SomethingSomething}' -FROM "JsonEntitiesBasic" AS j -"""); - } - - public override async Task Json_projection_with_deduplication_reverse_order(bool async) - { - await base.Json_projection_with_deduplication_reverse_order(async); - - AssertSql( - """ -SELECT j."OwnedReferenceRoot" #> '{OwnedReferenceBranch,OwnedReferenceLeaf}', j."Id", j."OwnedReferenceRoot", j."EntityBasicId", j."Name", j."OwnedCollectionRoot", j."OwnedReferenceRoot" -FROM "JsonEntitiesBasic" AS j -"""); - } - public override async Task Json_property_in_predicate(bool async) { await base.Json_property_in_predicate(async); @@ -666,24 +510,6 @@ public override async Task Left_join_json_entities_complex_projection_json_being """); } - public override async Task Project_json_entity_FirstOrDefault_subquery(bool async) - { - await base.Project_json_entity_FirstOrDefault_subquery(async); - - AssertSql( - """ -SELECT j1.c, j1."Id" -FROM "JsonEntitiesBasic" AS j -LEFT JOIN LATERAL ( - SELECT j0."OwnedReferenceRoot" -> 'OwnedReferenceBranch' AS c, j0."Id" - FROM "JsonEntitiesBasic" AS j0 - ORDER BY j0."Id" NULLS FIRST - LIMIT 1 -) AS j1 ON TRUE -ORDER BY j."Id" NULLS FIRST -"""); - } - public override async Task Project_json_entity_FirstOrDefault_subquery_with_binding_on_top(bool async) { await base.Project_json_entity_FirstOrDefault_subquery_with_binding_on_top(async); @@ -708,60 +534,6 @@ public override async Task Project_json_entity_FirstOrDefault_subquery_with_enti @""); } - public override async Task Project_json_entity_FirstOrDefault_subquery_deduplication(bool async) - { - await base.Project_json_entity_FirstOrDefault_subquery_deduplication(async); - - AssertSql( - """ -SELECT j1.c, j1."Id", j1.c0, j1."Id0", j1.c1, j1.c2, j1.c3, j1.c4 -FROM "JsonEntitiesBasic" AS j -LEFT JOIN LATERAL ( - SELECT j."OwnedReferenceRoot" -> 'OwnedCollectionBranch' AS c, j."Id", j0."OwnedReferenceRoot" AS c0, j0."Id" AS "Id0", j0."OwnedReferenceRoot" -> 'OwnedReferenceBranch' AS c1, j0."OwnedReferenceRoot" ->> 'Name' AS c2, CAST(j."OwnedReferenceRoot" #>> '{OwnedReferenceBranch,Enum}' AS integer) AS c3, 1 AS c4 - FROM "JsonEntitiesBasic" AS j0 - ORDER BY j0."Id" NULLS FIRST - LIMIT 1 -) AS j1 ON TRUE -ORDER BY j."Id" NULLS FIRST -"""); - } - - public override async Task Project_json_entity_FirstOrDefault_subquery_deduplication_and_outer_reference(bool async) - { - await base.Project_json_entity_FirstOrDefault_subquery_deduplication_and_outer_reference(async); - - AssertSql( - """ -SELECT j1.c, j1."Id", j1.c0, j1."Id0", j1.c1, j1.c2, j1.c3, j1.c4 -FROM "JsonEntitiesBasic" AS j -LEFT JOIN LATERAL ( - SELECT j."OwnedReferenceRoot" -> 'OwnedCollectionBranch' AS c, j."Id", j0."OwnedReferenceRoot" AS c0, j0."Id" AS "Id0", j0."OwnedReferenceRoot" -> 'OwnedReferenceBranch' AS c1, j0."OwnedReferenceRoot" ->> 'Name' AS c2, CAST(j."OwnedReferenceRoot" #>> '{OwnedReferenceBranch,Enum}' AS integer) AS c3, 1 AS c4 - FROM "JsonEntitiesBasic" AS j0 - ORDER BY j0."Id" NULLS FIRST - LIMIT 1 -) AS j1 ON TRUE -ORDER BY j."Id" NULLS FIRST -"""); - } - - public override async Task Project_json_entity_FirstOrDefault_subquery_deduplication_outer_reference_and_pruning(bool async) - { - await base.Project_json_entity_FirstOrDefault_subquery_deduplication_outer_reference_and_pruning(async); - - AssertSql( - """ -SELECT j1.c, j1."Id", j1.c0 -FROM "JsonEntitiesBasic" AS j -LEFT JOIN LATERAL ( - SELECT j."OwnedReferenceRoot" -> 'OwnedCollectionBranch' AS c, j."Id", 1 AS c0 - FROM "JsonEntitiesBasic" AS j0 - ORDER BY j0."Id" NULLS FIRST - LIMIT 1 -) AS j1 ON TRUE -ORDER BY j."Id" NULLS FIRST -"""); - } - public override async Task Json_entity_with_inheritance_basic_projection(bool async) { await base.Json_entity_with_inheritance_basic_projection(async); @@ -1652,48 +1424,6 @@ FROM ROWS FROM (jsonb_to_recordset(j."OwnedReferenceRoot" #> '{OwnedReferenceBra """); } - public override async Task Json_collection_SelectMany(bool async) - { - await base.Json_collection_SelectMany(async); - - AssertSql( - """ -SELECT j."Id", o."Id", o."Name", o."Names", o."Number", o."Numbers", o."OwnedCollectionBranch", o."OwnedReferenceBranch" -FROM "JsonEntitiesBasic" AS j -JOIN LATERAL ROWS FROM (jsonb_to_recordset(j."OwnedCollectionRoot") AS ( - "Id" integer, - "Name" text, - "Names" text[], - "Number" integer, - "Numbers" integer[], - "OwnedCollectionBranch" jsonb, - "OwnedReferenceBranch" jsonb -)) WITH ORDINALITY AS o ON TRUE -"""); - } - - public override async Task Json_nested_collection_SelectMany(bool async) - { - await base.Json_nested_collection_SelectMany(async); - - AssertSql( - """ -SELECT j."Id", o."Date", o."Enum", o."Enums", o."Fraction", o."Id", o."NullableEnum", o."NullableEnums", o."OwnedCollectionLeaf", o."OwnedReferenceLeaf" -FROM "JsonEntitiesBasic" AS j -JOIN LATERAL ROWS FROM (jsonb_to_recordset(j."OwnedReferenceRoot" -> 'OwnedCollectionBranch') AS ( - "Date" timestamp without time zone, - "Enum" integer, - "Enums" integer[], - "Fraction" numeric(18,2), - "Id" integer, - "NullableEnum" integer, - "NullableEnums" integer[], - "OwnedCollectionLeaf" jsonb, - "OwnedReferenceLeaf" jsonb -)) WITH ORDINALITY AS o ON TRUE -"""); - } - public override async Task Json_collection_of_primitives_SelectMany(bool async) { await base.Json_collection_of_primitives_SelectMany(async); diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/ComplexRelationshipsNpgsqlFixture.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/ComplexRelationshipsNpgsqlFixture.cs new file mode 100644 index 0000000000..7d5c735cf4 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/ComplexRelationshipsNpgsqlFixture.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships; + +public class ComplexRelationshipsNpgsqlFixture : ComplexRelationsjipsRelationalFixtureBase, ITestSqlLoggerFactory +{ + protected override ITestStoreFactory TestStoreFactory + => NpgsqlTestStoreFactory.Instance; + + public TestSqlLoggerFactory TestSqlLoggerFactory + => (TestSqlLoggerFactory)ListLoggerFactory; +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/Include/NavigationIncludeNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/Include/NavigationIncludeNpgsqlTest.cs new file mode 100644 index 0000000000..d06efe57b9 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/Include/NavigationIncludeNpgsqlTest.cs @@ -0,0 +1,150 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit.Sdk; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Include; + +public class NavigationIncludeNpgsqlTest + : NavigationIncludeRelationalTestBase +{ + public NavigationIncludeNpgsqlTest(NavigationRelationshipsNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Include_trunk_required(bool async) + { + await base.Include_trunk_required(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId", t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +"""); + } + + public override async Task Include_trunk_optional(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.Include_trunk_optional(async)); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId", t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId" +FROM "RootEntities" AS r +LEFT JOIN "TrunkEntities" AS t ON r."OptionalReferenceTrunkId" = t."Id" +"""); + } + + public override async Task Include_trunk_collection(bool async) + { + await base.Include_trunk_collection(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId", t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId" +FROM "RootEntities" AS r +LEFT JOIN "TrunkEntities" AS t ON r."Id" = t."CollectionRootId" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Include_trunk_required_optional_and_collection(bool async) + { + await base.Include_trunk_required_optional_and_collection(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId", t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", t0."Id", t0."CollectionRootId", t0."Name", t0."OptionalReferenceBranchId", t0."RequiredReferenceBranchId", t1."Id", t1."CollectionRootId", t1."Name", t1."OptionalReferenceBranchId", t1."RequiredReferenceBranchId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +LEFT JOIN "TrunkEntities" AS t0 ON r."OptionalReferenceTrunkId" = t0."Id" +LEFT JOIN "TrunkEntities" AS t1 ON r."Id" = t1."CollectionRootId" +ORDER BY r."Id" NULLS FIRST, t."Id" NULLS FIRST, t0."Id" NULLS FIRST +"""); + } + + public override async Task Include_branch_required_required(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.Include_branch_required_required(async)); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId", t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +INNER JOIN "BranchEntities" AS b ON t."RequiredReferenceBranchId" = b."Id" +"""); + } + + public override async Task Include_branch_required_collection(bool async) + { + await base.Include_branch_required_collection(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId", t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +LEFT JOIN "BranchEntities" AS b ON t."Id" = b."CollectionTrunkId" +ORDER BY r."Id" NULLS FIRST, t."Id" NULLS FIRST +"""); + } + + public override async Task Include_branch_optional_optional(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.Include_branch_optional_optional(async)); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId", t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +LEFT JOIN "TrunkEntities" AS t ON r."OptionalReferenceTrunkId" = t."Id" +LEFT JOIN "BranchEntities" AS b ON t."OptionalReferenceBranchId" = b."Id" +"""); + } + + public override async Task Include_branch_optional_collection(bool async) + { + await base.Include_branch_optional_collection(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId", t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +LEFT JOIN "TrunkEntities" AS t ON r."OptionalReferenceTrunkId" = t."Id" +LEFT JOIN "BranchEntities" AS b ON t."Id" = b."CollectionTrunkId" +ORDER BY r."Id" NULLS FIRST, t."Id" NULLS FIRST +"""); + } + + public override async Task Include_branch_collection_collection(bool async) + { + await base.Include_branch_collection_collection(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId", s."Id", s."CollectionRootId", s."Name", s."OptionalReferenceBranchId", s."RequiredReferenceBranchId", s."Id0", s."CollectionTrunkId", s."Name0", s."OptionalReferenceLeafId", s."RequiredReferenceLeafId" +FROM "RootEntities" AS r +LEFT JOIN ( + SELECT t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", b."Id" AS "Id0", b."CollectionTrunkId", b."Name" AS "Name0", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" + FROM "TrunkEntities" AS t + LEFT JOIN "BranchEntities" AS b ON t."Id" = b."CollectionTrunkId" +) AS s ON r."Id" = s."CollectionRootId" +ORDER BY r."Id" NULLS FIRST, s."Id" NULLS FIRST +"""); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/NavigationRelationshipsNpgsqlFixture.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/NavigationRelationshipsNpgsqlFixture.cs new file mode 100644 index 0000000000..013b998710 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/NavigationRelationshipsNpgsqlFixture.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships; + +public class NavigationRelationshipsNpgsqlFixture : NavigationRelationshipsRelationalFixtureBase, ITestSqlLoggerFactory +{ + protected override ITestStoreFactory TestStoreFactory + => NpgsqlTestStoreFactory.Instance; + + public TestSqlLoggerFactory TestSqlLoggerFactory + => (TestSqlLoggerFactory)ListLoggerFactory; +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/OwnedJsonRelationshipsNpgsqlFixture.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/OwnedJsonRelationshipsNpgsqlFixture.cs new file mode 100644 index 0000000000..d6cac1b51a --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/OwnedJsonRelationshipsNpgsqlFixture.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships; + +public class OwnedJsonRelationshipsNpgsqlFixture : OwnedJsonRelationshipsRelationalFixtureBase, ITestSqlLoggerFactory +{ + protected override ITestStoreFactory TestStoreFactory + => NpgsqlTestStoreFactory.Instance; + + public TestSqlLoggerFactory TestSqlLoggerFactory + => (TestSqlLoggerFactory)ListLoggerFactory; +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/OwnedJsonTypeRelationshipsNpgsqlFixture.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/OwnedJsonTypeRelationshipsNpgsqlFixture.cs new file mode 100644 index 0000000000..6215caca11 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/OwnedJsonTypeRelationshipsNpgsqlFixture.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.EntityFrameworkCore.TestModels.RelationshipsModel; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships; + +public class OwnedJsonTypeRelationshipsNpgsqlFixture : OwnedJsonRelationshipsRelationalFixtureBase, ITestSqlLoggerFactory +{ + protected override string StoreName => "JsonTypeRelationshipsQueryTest"; + + protected override ITestStoreFactory TestStoreFactory + => NpgsqlTestStoreFactory.Instance; + + public TestSqlLoggerFactory TestSqlLoggerFactory + => (TestSqlLoggerFactory)ListLoggerFactory; + + protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) + { + base.OnModelCreating(modelBuilder, context); + + modelBuilder.Entity().OwnsOne(x => x.RequiredReferenceTrunk).HasColumnType("json"); + modelBuilder.Entity().OwnsOne(x => x.OptionalReferenceTrunk).HasColumnType("json"); + modelBuilder.Entity().OwnsMany(x => x.CollectionTrunk).HasColumnType("json"); + } +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/OwnedRelationshipsNpgsqlFixture.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/OwnedRelationshipsNpgsqlFixture.cs new file mode 100644 index 0000000000..f4fd46c711 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/OwnedRelationshipsNpgsqlFixture.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships; + +public class OwnedRelationshipsNpgsqlFixture : OwnedRelationshipsRelationalFixtureBase, ITestSqlLoggerFactory +{ + protected override ITestStoreFactory TestStoreFactory + => NpgsqlTestStoreFactory.Instance; + + public TestSqlLoggerFactory TestSqlLoggerFactory + => (TestSqlLoggerFactory)ListLoggerFactory; +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/OwnedTableSplittingRelationshipsNpgsqlFixture.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/OwnedTableSplittingRelationshipsNpgsqlFixture.cs new file mode 100644 index 0000000000..0401738eef --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/OwnedTableSplittingRelationshipsNpgsqlFixture.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships; + +public class OwnedTableSplittingRelationshipsNpgsqlFixture : OwnedTableSplittingRelationshipsRelationalFixtureBase, ITestSqlLoggerFactory +{ + protected override ITestStoreFactory TestStoreFactory + => NpgsqlTestStoreFactory.Instance; + + public TestSqlLoggerFactory TestSqlLoggerFactory + => (TestSqlLoggerFactory)ListLoggerFactory; +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/ComplexNoTrackingProjectionNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/ComplexNoTrackingProjectionNpgsqlTest.cs new file mode 100644 index 0000000000..23842cf261 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/ComplexNoTrackingProjectionNpgsqlTest.cs @@ -0,0 +1,156 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Projection; + +public class ComplexNoTrackingProjectionNpgsqlTest + : ComplexNoTrackingProjectionRelationalTestBase +{ + public ComplexNoTrackingProjectionNpgsqlTest(ComplexRelationshipsNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Select_everything(bool async) + { + await base.Select_everything(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId", t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", t."RequiredReferenceBranch_Name", t."RequiredReferenceBranch_RequiredReferenceLeaf_Name" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."Id" = t."Id" +"""); + } + + public override async Task Select_root(bool async) + { + await base.Select_root(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId" +FROM "RootEntities" AS r +"""); + } + + public override async Task Select_trunk_optional(bool async) + { + await base.Select_trunk_optional(async); + + AssertSql( + """ +SELECT t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", t."RequiredReferenceBranch_Name", t."RequiredReferenceBranch_RequiredReferenceLeaf_Name" +FROM "RootEntities" AS r +LEFT JOIN "TrunkEntities" AS t ON r."OptionalReferenceTrunkId" = t."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_trunk_required(bool async) + { + await base.Select_trunk_required(async); + + AssertSql( + """ +SELECT t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", t."RequiredReferenceBranch_Name", t."RequiredReferenceBranch_RequiredReferenceLeaf_Name" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_required_required(bool async) + { + await base.Select_branch_required_required(async); + + AssertSql( + """ +SELECT t."RequiredReferenceBranch_Name", t."RequiredReferenceBranch_RequiredReferenceLeaf_Name" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_required_optional(bool async) + { + await base.Select_branch_required_optional(async); + + AssertSql(); + } + + public override async Task Select_branch_optional_required(bool async) + { + await base.Select_branch_optional_required(async); + + AssertSql(); + } + + public override async Task Select_branch_optional_optional(bool async) + { + await base.Select_branch_optional_optional(async); + + AssertSql(); + } + + public override async Task Select_root_duplicated(bool async) + { + await base.Select_root_duplicated(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId" +FROM "RootEntities" AS r +"""); + } + + public override async Task Select_trunk_and_branch_duplicated(bool async) + { + await base.Select_trunk_and_branch_duplicated(async); + + AssertSql(); + } + + public override async Task Select_trunk_and_trunk_duplicated(bool async) + { + await base.Select_trunk_and_trunk_duplicated(async); + + AssertSql(); + } + + public override async Task Select_leaf_trunk_root(bool async) + { + await base.Select_leaf_trunk_root(async); + + AssertSql( + """ +SELECT t."RequiredReferenceBranch_RequiredReferenceLeaf_Name", t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", t."RequiredReferenceBranch_Name", r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +"""); + } + + public override async Task Select_subquery_root_set_required_trunk_FirstOrDefault_branch(bool async) + { + await base.Select_subquery_root_set_required_trunk_FirstOrDefault_branch(async); + + AssertSql(); + } + + public override async Task Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(bool async) + { + await base.Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(async); + + AssertSql(); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/ComplexProjectionNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/ComplexProjectionNpgsqlTest.cs new file mode 100644 index 0000000000..f9c7e8c290 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/ComplexProjectionNpgsqlTest.cs @@ -0,0 +1,156 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Projection; + +public class ComplexProjectionNpgsqlTest + : ComplexProjectionRelationalTestBase +{ + public ComplexProjectionNpgsqlTest(ComplexRelationshipsNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Select_everything(bool async) + { + await base.Select_everything(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId", t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", t."RequiredReferenceBranch_Name", t."RequiredReferenceBranch_RequiredReferenceLeaf_Name" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."Id" = t."Id" +"""); + } + + public override async Task Select_root(bool async) + { + await base.Select_root(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId" +FROM "RootEntities" AS r +"""); + } + + public override async Task Select_trunk_optional(bool async) + { + await base.Select_trunk_optional(async); + + AssertSql( + """ +SELECT t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", t."RequiredReferenceBranch_Name", t."RequiredReferenceBranch_RequiredReferenceLeaf_Name" +FROM "RootEntities" AS r +LEFT JOIN "TrunkEntities" AS t ON r."OptionalReferenceTrunkId" = t."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_trunk_required(bool async) + { + await base.Select_trunk_required(async); + + AssertSql( + """ +SELECT t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", t."RequiredReferenceBranch_Name", t."RequiredReferenceBranch_RequiredReferenceLeaf_Name" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_required_required(bool async) + { + await base.Select_branch_required_required(async); + + AssertSql( + """ +SELECT t."RequiredReferenceBranch_Name", t."RequiredReferenceBranch_RequiredReferenceLeaf_Name" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_required_optional(bool async) + { + await base.Select_branch_required_optional(async); + + AssertSql(); + } + + public override async Task Select_branch_optional_required(bool async) + { + await base.Select_branch_optional_required(async); + + AssertSql(); + } + + public override async Task Select_branch_optional_optional(bool async) + { + await base.Select_branch_optional_optional(async); + + AssertSql(); + } + + public override async Task Select_root_duplicated(bool async) + { + await base.Select_root_duplicated(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId" +FROM "RootEntities" AS r +"""); + } + + public override async Task Select_trunk_and_branch_duplicated(bool async) + { + await base.Select_trunk_and_branch_duplicated(async); + + AssertSql(); + } + + public override async Task Select_trunk_and_trunk_duplicated(bool async) + { + await base.Select_trunk_and_trunk_duplicated(async); + + AssertSql(); + } + + public override async Task Select_leaf_trunk_root(bool async) + { + await base.Select_leaf_trunk_root(async); + + AssertSql( + """ +SELECT t."RequiredReferenceBranch_RequiredReferenceLeaf_Name", t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", t."RequiredReferenceBranch_Name", r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +"""); + } + + public override async Task Select_subquery_root_set_required_trunk_FirstOrDefault_branch(bool async) + { + await base.Select_subquery_root_set_required_trunk_FirstOrDefault_branch(async); + + AssertSql(); + } + + public override async Task Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(bool async) + { + await base.Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(async); + + AssertSql(); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/NavigationNoTrackingProjectionNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/NavigationNoTrackingProjectionNpgsqlTest.cs new file mode 100644 index 0000000000..1c238615f4 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/NavigationNoTrackingProjectionNpgsqlTest.cs @@ -0,0 +1,198 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using Xunit.Sdk; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Projection; + +public class NavigationNoTrackingProjectionNpgsqlTest + : NavigationNoTrackingProjectionRelationalTestBase +{ + public NavigationNoTrackingProjectionNpgsqlTest(NavigationRelationshipsNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Select_everything_using_joins(bool async) + { + await base.Select_everything_using_joins(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId", t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId", l."Id", l."CollectionBranchId", l."Name" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."Id" = t."Id" +INNER JOIN "BranchEntities" AS b ON t."Id" = b."Id" +INNER JOIN "LeafEntities" AS l ON b."Id" = l."Id" +"""); + } + + public override async Task Select_trunk_collection(bool async) + { + await base.Select_trunk_collection(async); + AssertSql( + """ +SELECT r."Id", t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId" +FROM "RootEntities" AS r +LEFT JOIN "TrunkEntities" AS t ON r."Id" = t."CollectionRootId" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_required_collection(bool async) + { + await base.Select_branch_required_collection(async); + + AssertSql( + """ +SELECT r."Id", t."Id", b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +LEFT JOIN "BranchEntities" AS b ON t."Id" = b."CollectionTrunkId" +ORDER BY r."Id" NULLS FIRST, t."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_optional_collection(bool async) + { + await base.Select_branch_optional_collection(async); + + AssertSql( + """ +SELECT r."Id", t."Id", b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +LEFT JOIN "BranchEntities" AS b ON t."Id" = b."CollectionTrunkId" +ORDER BY r."Id" NULLS FIRST, t."Id" NULLS FIRST +"""); + } + + public override async Task Select_multiple_branch_leaf(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.Select_multiple_branch_leaf(async)); + + AssertSql( + """ +SELECT r."Id", b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId", l."Id", l."CollectionBranchId", l."Name", t."Id", l0."Id", l0."CollectionBranchId", l0."Name", b0."Id", b0."CollectionTrunkId", b0."Name", b0."OptionalReferenceLeafId", b0."RequiredReferenceLeafId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +INNER JOIN "BranchEntities" AS b ON t."RequiredReferenceBranchId" = b."Id" +LEFT JOIN "LeafEntities" AS l ON b."OptionalReferenceLeafId" = l."Id" +LEFT JOIN "LeafEntities" AS l0 ON b."Id" = l0."CollectionBranchId" +LEFT JOIN "BranchEntities" AS b0 ON t."Id" = b0."CollectionTrunkId" +ORDER BY r."Id" NULLS FIRST, t."Id" NULLS FIRST, b."Id" NULLS FIRST, l."Id" NULLS FIRST, l0."Id" NULLS FIRST +"""); + } + + public override async Task Select_subquery_root_set_trunk_FirstOrDefault_collection(bool async) + { + await base.Select_subquery_root_set_trunk_FirstOrDefault_collection(async); + + AssertSql( + """ +SELECT r."Id", s."Id", s."Id0", b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId", s.c +FROM "RootEntities" AS r +LEFT JOIN LATERAL ( + SELECT 1 AS c, r0."Id", t."Id" AS "Id0" + FROM "RootEntities" AS r0 + INNER JOIN "TrunkEntities" AS t ON r0."RequiredReferenceTrunkId" = t."Id" + ORDER BY r0."Id" NULLS FIRST + LIMIT 1 +) AS s ON TRUE +LEFT JOIN "BranchEntities" AS b ON s."Id0" = b."CollectionTrunkId" +ORDER BY r."Id" NULLS FIRST, s."Id" NULLS FIRST, s."Id0" NULLS FIRST +"""); + } + + public override async Task Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(bool async) + { + await base.Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(async); + + AssertSql( + """ +SELECT r."Id", t."Id", b."Id", s."Id1", s."Id", s."Id0", b1."Id", b1."CollectionTrunkId", b1."Name", b1."OptionalReferenceLeafId", b1."RequiredReferenceLeafId", s."CollectionRootId", s."Name", s."OptionalReferenceBranchId", s."RequiredReferenceBranchId", s."CollectionTrunkId", s."Name0", s."OptionalReferenceLeafId", s."RequiredReferenceLeafId", s."Name1", s.c +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +INNER JOIN "BranchEntities" AS b ON t."RequiredReferenceBranchId" = b."Id" +LEFT JOIN LATERAL ( + SELECT t0."Id", t0."CollectionRootId", t0."Name", t0."OptionalReferenceBranchId", t0."RequiredReferenceBranchId", b0."Id" AS "Id0", b0."CollectionTrunkId", b0."Name" AS "Name0", b0."OptionalReferenceLeafId", b0."RequiredReferenceLeafId", b."Name" AS "Name1", 1 AS c, r0."Id" AS "Id1" + FROM "RootEntities" AS r0 + INNER JOIN "TrunkEntities" AS t0 ON r0."RequiredReferenceTrunkId" = t0."Id" + INNER JOIN "BranchEntities" AS b0 ON t0."RequiredReferenceBranchId" = b0."Id" + ORDER BY r0."Id" NULLS FIRST + LIMIT 1 +) AS s ON TRUE +LEFT JOIN "BranchEntities" AS b1 ON t."Id" = b1."CollectionTrunkId" +ORDER BY r."Id" NULLS FIRST, t."Id" NULLS FIRST, b."Id" NULLS FIRST, s."Id1" NULLS FIRST, s."Id" NULLS FIRST, s."Id0" NULLS FIRST +"""); + } + + public override async Task Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(bool async) + { + await base.Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(async); + + AssertSql( + """ +SELECT r."Id", t."Id", r1."Id", b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId", r1.c +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +LEFT JOIN LATERAL ( + SELECT 1 AS c, r0."Id" + FROM "RootEntities" AS r0 + ORDER BY r0."Id" NULLS FIRST + LIMIT 1 +) AS r1 ON TRUE +LEFT JOIN "BranchEntities" AS b ON t."Id" = b."CollectionTrunkId" +ORDER BY r."Id" NULLS FIRST, t."Id" NULLS FIRST, r1."Id" NULLS FIRST +"""); + } + + public override async Task SelectMany_trunk_collection(bool async) + { + await base.SelectMany_trunk_collection(async); + + AssertSql( + """ +SELECT t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."Id" = t."CollectionRootId" +"""); + } + + public override async Task SelectMany_required_trunk_reference_branch_collection(bool async) + { + await base.SelectMany_required_trunk_reference_branch_collection(async); + + AssertSql( + """ +SELECT b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +INNER JOIN "BranchEntities" AS b ON t."Id" = b."CollectionTrunkId" +"""); + } + + public override async Task SelectMany_optional_trunk_reference_branch_collection(bool async) + { + await base.SelectMany_optional_trunk_reference_branch_collection(async); + + AssertSql( + """ +SELECT b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +LEFT JOIN "TrunkEntities" AS t ON r."OptionalReferenceTrunkId" = t."Id" +INNER JOIN "BranchEntities" AS b ON t."Id" = b."CollectionTrunkId" +"""); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/NavigationProjectionNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/NavigationProjectionNpgsqlTest.cs new file mode 100644 index 0000000000..a3217c1d5e --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/NavigationProjectionNpgsqlTest.cs @@ -0,0 +1,198 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit.Sdk; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Projection; + +public class NavigationProjectionNpgsqlTest + : NavigationProjectionRelationalTestBase +{ + public NavigationProjectionNpgsqlTest(NavigationRelationshipsNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Select_everything_using_joins(bool async) + { + await base.Select_everything_using_joins(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId", t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId", l."Id", l."CollectionBranchId", l."Name" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."Id" = t."Id" +INNER JOIN "BranchEntities" AS b ON t."Id" = b."Id" +INNER JOIN "LeafEntities" AS l ON b."Id" = l."Id" +"""); + } + + public override async Task Select_trunk_collection(bool async) + { + await base.Select_trunk_collection(async); + + AssertSql( + """ +SELECT r."Id", t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId" +FROM "RootEntities" AS r +LEFT JOIN "TrunkEntities" AS t ON r."Id" = t."CollectionRootId" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_required_collection(bool async) + { + await base.Select_branch_required_collection(async); + + AssertSql( + """ +SELECT r."Id", t."Id", b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +LEFT JOIN "BranchEntities" AS b ON t."Id" = b."CollectionTrunkId" +ORDER BY r."Id" NULLS FIRST, t."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_optional_collection(bool async) + { + await base.Select_branch_optional_collection(async); + + AssertSql( + """ +SELECT r."Id", t."Id", b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +LEFT JOIN "BranchEntities" AS b ON t."Id" = b."CollectionTrunkId" +ORDER BY r."Id" NULLS FIRST, t."Id" NULLS FIRST +"""); + } + + public override async Task Select_multiple_branch_leaf(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.Select_multiple_branch_leaf(async)); + + AssertSql( + """ +SELECT r."Id", b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId", l."Id", l."CollectionBranchId", l."Name", t."Id", l0."Id", l0."CollectionBranchId", l0."Name", b0."Id", b0."CollectionTrunkId", b0."Name", b0."OptionalReferenceLeafId", b0."RequiredReferenceLeafId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +INNER JOIN "BranchEntities" AS b ON t."RequiredReferenceBranchId" = b."Id" +LEFT JOIN "LeafEntities" AS l ON b."OptionalReferenceLeafId" = l."Id" +LEFT JOIN "LeafEntities" AS l0 ON b."Id" = l0."CollectionBranchId" +LEFT JOIN "BranchEntities" AS b0 ON t."Id" = b0."CollectionTrunkId" +ORDER BY r."Id" NULLS FIRST, t."Id" NULLS FIRST, b."Id" NULLS FIRST, l."Id" NULLS FIRST, l0."Id" NULLS FIRST +"""); + } + + public override async Task Select_subquery_root_set_trunk_FirstOrDefault_collection(bool async) + { + await base.Select_subquery_root_set_trunk_FirstOrDefault_collection(async); + + AssertSql( + """ +SELECT r."Id", s."Id", s."Id0", b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId", s.c +FROM "RootEntities" AS r +LEFT JOIN LATERAL ( + SELECT 1 AS c, r0."Id", t."Id" AS "Id0" + FROM "RootEntities" AS r0 + INNER JOIN "TrunkEntities" AS t ON r0."RequiredReferenceTrunkId" = t."Id" + ORDER BY r0."Id" NULLS FIRST + LIMIT 1 +) AS s ON TRUE +LEFT JOIN "BranchEntities" AS b ON s."Id0" = b."CollectionTrunkId" +ORDER BY r."Id" NULLS FIRST, s."Id" NULLS FIRST, s."Id0" NULLS FIRST +"""); + } + + public override async Task Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(bool async) + { + await base.Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(async); + + AssertSql( + """ +SELECT r."Id", t."Id", b."Id", s."Id1", s."Id", s."Id0", b1."Id", b1."CollectionTrunkId", b1."Name", b1."OptionalReferenceLeafId", b1."RequiredReferenceLeafId", s."CollectionRootId", s."Name", s."OptionalReferenceBranchId", s."RequiredReferenceBranchId", s."CollectionTrunkId", s."Name0", s."OptionalReferenceLeafId", s."RequiredReferenceLeafId", s."Name1", s.c +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +INNER JOIN "BranchEntities" AS b ON t."RequiredReferenceBranchId" = b."Id" +LEFT JOIN LATERAL ( + SELECT t0."Id", t0."CollectionRootId", t0."Name", t0."OptionalReferenceBranchId", t0."RequiredReferenceBranchId", b0."Id" AS "Id0", b0."CollectionTrunkId", b0."Name" AS "Name0", b0."OptionalReferenceLeafId", b0."RequiredReferenceLeafId", b."Name" AS "Name1", 1 AS c, r0."Id" AS "Id1" + FROM "RootEntities" AS r0 + INNER JOIN "TrunkEntities" AS t0 ON r0."RequiredReferenceTrunkId" = t0."Id" + INNER JOIN "BranchEntities" AS b0 ON t0."RequiredReferenceBranchId" = b0."Id" + ORDER BY r0."Id" NULLS FIRST + LIMIT 1 +) AS s ON TRUE +LEFT JOIN "BranchEntities" AS b1 ON t."Id" = b1."CollectionTrunkId" +ORDER BY r."Id" NULLS FIRST, t."Id" NULLS FIRST, b."Id" NULLS FIRST, s."Id1" NULLS FIRST, s."Id" NULLS FIRST, s."Id0" NULLS FIRST +"""); + } + + public override async Task Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(bool async) + { + await base.Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(async); + + AssertSql( + """ +SELECT r."Id", t."Id", r1."Id", b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId", r1.c +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +LEFT JOIN LATERAL ( + SELECT 1 AS c, r0."Id" + FROM "RootEntities" AS r0 + ORDER BY r0."Id" NULLS FIRST + LIMIT 1 +) AS r1 ON TRUE +LEFT JOIN "BranchEntities" AS b ON t."Id" = b."CollectionTrunkId" +ORDER BY r."Id" NULLS FIRST, t."Id" NULLS FIRST, r1."Id" NULLS FIRST +"""); + } + + public override async Task SelectMany_trunk_collection(bool async) + { + await base.SelectMany_trunk_collection(async); + + AssertSql( + """ +SELECT t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."Id" = t."CollectionRootId" +"""); + } + + public override async Task SelectMany_required_trunk_reference_branch_collection(bool async) + { + await base.SelectMany_required_trunk_reference_branch_collection(async); + + AssertSql( + """ +SELECT b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +INNER JOIN "BranchEntities" AS b ON t."Id" = b."CollectionTrunkId" +"""); + } + + public override async Task SelectMany_optional_trunk_reference_branch_collection(bool async) + { + await base.SelectMany_optional_trunk_reference_branch_collection(async); + + AssertSql( + """ +SELECT b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +LEFT JOIN "TrunkEntities" AS t ON r."OptionalReferenceTrunkId" = t."Id" +INNER JOIN "BranchEntities" AS b ON t."Id" = b."CollectionTrunkId" +"""); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/NavigationReferenceNoTrackingProjectionNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/NavigationReferenceNoTrackingProjectionNpgsqlTest.cs new file mode 100644 index 0000000000..ddbf99ab94 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/NavigationReferenceNoTrackingProjectionNpgsqlTest.cs @@ -0,0 +1,223 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Projection; + +public class NavigationReferenceNoTrackingProjectionSqlServerTest + : NavigationReferenceNoTrackingProjectionRelationalTestBase +{ + public NavigationReferenceNoTrackingProjectionSqlServerTest(NavigationRelationshipsNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Select_everything(bool async) + { + await base.Select_everything(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId", t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId", l."Id", l."CollectionBranchId", l."Name" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."Id" = t."Id" +INNER JOIN "BranchEntities" AS b ON t."Id" = b."Id" +INNER JOIN "LeafEntities" AS l ON b."Id" = l."Id" +"""); + } + + public override async Task Select_root(bool async) + { + await base.Select_root(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId" +FROM "RootEntities" AS r +"""); + } + + public override async Task Select_trunk_optional(bool async) + { + await base.Select_trunk_optional(async); + + AssertSql( + """ +SELECT t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId" +FROM "RootEntities" AS r +LEFT JOIN "TrunkEntities" AS t ON r."OptionalReferenceTrunkId" = t."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_trunk_required(bool async) + { + await base.Select_trunk_required(async); + + AssertSql( + """ +SELECT t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_required_required(bool async) + { + await base.Select_branch_required_required(async); + + AssertSql( + """ +SELECT b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +INNER JOIN "BranchEntities" AS b ON t."RequiredReferenceBranchId" = b."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_required_optional(bool async) + { + await base.Select_branch_required_optional(async); + + AssertSql( + """ +SELECT b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +LEFT JOIN "BranchEntities" AS b ON t."OptionalReferenceBranchId" = b."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_optional_required(bool async) + { + await base.Select_branch_optional_required(async); + + AssertSql( + """ +SELECT b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +INNER JOIN "BranchEntities" AS b ON t."RequiredReferenceBranchId" = b."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_optional_optional(bool async) + { + await base.Select_branch_optional_optional(async); + + AssertSql( + """ +SELECT b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +LEFT JOIN "BranchEntities" AS b ON t."OptionalReferenceBranchId" = b."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_root_duplicated(bool async) + { + await base.Select_root_duplicated(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId" +FROM "RootEntities" AS r +"""); + } + + public override async Task Select_trunk_and_branch_duplicated(bool async) + { + await base.Select_trunk_and_branch_duplicated(async); + + AssertSql( + """ +SELECT t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +LEFT JOIN "TrunkEntities" AS t ON r."OptionalReferenceTrunkId" = t."Id" +LEFT JOIN "BranchEntities" AS b ON t."RequiredReferenceBranchId" = b."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_trunk_and_trunk_duplicated(bool async) + { + await base.Select_trunk_and_trunk_duplicated(async); + + AssertSql( + """ +SELECT t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", l."Id", l."CollectionBranchId", l."Name" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +LEFT JOIN "BranchEntities" AS b ON t."OptionalReferenceBranchId" = b."Id" +LEFT JOIN "LeafEntities" AS l ON b."RequiredReferenceLeafId" = l."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_leaf_trunk_root(bool async) + { + await base.Select_leaf_trunk_root(async); + + AssertSql( + """ +SELECT l."Id", l."CollectionBranchId", l."Name", t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +INNER JOIN "BranchEntities" AS b ON t."RequiredReferenceBranchId" = b."Id" +INNER JOIN "LeafEntities" AS l ON b."RequiredReferenceLeafId" = l."Id" +"""); + } + + public override async Task Select_subquery_root_set_required_trunk_FirstOrDefault_branch(bool async) + { + await base.Select_subquery_root_set_required_trunk_FirstOrDefault_branch(async); + + AssertSql( + """ +SELECT s."Id", s."CollectionTrunkId", s."Name", s."OptionalReferenceLeafId", s."RequiredReferenceLeafId" +FROM "RootEntities" AS r +LEFT JOIN LATERAL ( + SELECT b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" + FROM "RootEntities" AS r0 + INNER JOIN "TrunkEntities" AS t ON r0."RequiredReferenceTrunkId" = t."Id" + INNER JOIN "BranchEntities" AS b ON t."RequiredReferenceBranchId" = b."Id" + ORDER BY r0."Id" NULLS FIRST + LIMIT 1 +) AS s ON TRUE +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(bool async) + { + await base.Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(async); + + AssertSql( + """ +SELECT s."Id", s."CollectionTrunkId", s."Name", s."OptionalReferenceLeafId", s."RequiredReferenceLeafId" +FROM "RootEntities" AS r +LEFT JOIN LATERAL ( + SELECT b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" + FROM "RootEntities" AS r0 + LEFT JOIN "TrunkEntities" AS t ON r0."OptionalReferenceTrunkId" = t."Id" + LEFT JOIN "BranchEntities" AS b ON t."OptionalReferenceBranchId" = b."Id" + ORDER BY r0."Id" NULLS FIRST + LIMIT 1 +) AS s ON TRUE +ORDER BY r."Id" NULLS FIRST +"""); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/NavigationReferenceProjectionNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/NavigationReferenceProjectionNpgsqlTest.cs new file mode 100644 index 0000000000..3f3ab33fd3 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/NavigationReferenceProjectionNpgsqlTest.cs @@ -0,0 +1,223 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Projection; + +public class NavigationReferenceProjectionNpgsqlTest + : NavigationReferenceProjectionRelationalTestBase +{ + public NavigationReferenceProjectionNpgsqlTest(NavigationRelationshipsNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Select_everything(bool async) + { + await base.Select_everything(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId", t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId", l."Id", l."CollectionBranchId", l."Name" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."Id" = t."Id" +INNER JOIN "BranchEntities" AS b ON t."Id" = b."Id" +INNER JOIN "LeafEntities" AS l ON b."Id" = l."Id" +"""); + } + + public override async Task Select_root(bool async) + { + await base.Select_root(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId" +FROM "RootEntities" AS r +"""); + } + + public override async Task Select_trunk_optional(bool async) + { + await base.Select_trunk_optional(async); + + AssertSql( + """ +SELECT t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId" +FROM "RootEntities" AS r +LEFT JOIN "TrunkEntities" AS t ON r."OptionalReferenceTrunkId" = t."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_trunk_required(bool async) + { + await base.Select_trunk_required(async); + + AssertSql( + """ +SELECT t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_required_required(bool async) + { + await base.Select_branch_required_required(async); + + AssertSql( + """ +SELECT b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +INNER JOIN "BranchEntities" AS b ON t."RequiredReferenceBranchId" = b."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_required_optional(bool async) + { + await base.Select_branch_required_optional(async); + + AssertSql( + """ +SELECT b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +LEFT JOIN "BranchEntities" AS b ON t."OptionalReferenceBranchId" = b."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_optional_required(bool async) + { + await base.Select_branch_optional_required(async); + + AssertSql( + """ +SELECT b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +INNER JOIN "BranchEntities" AS b ON t."RequiredReferenceBranchId" = b."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_optional_optional(bool async) + { + await base.Select_branch_optional_optional(async); + + AssertSql( + """ +SELECT b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +LEFT JOIN "BranchEntities" AS b ON t."OptionalReferenceBranchId" = b."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_root_duplicated(bool async) + { + await base.Select_root_duplicated(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId" +FROM "RootEntities" AS r +"""); + } + + public override async Task Select_trunk_and_branch_duplicated(bool async) + { + await base.Select_trunk_and_branch_duplicated(async); + + AssertSql( + """ +SELECT t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" +FROM "RootEntities" AS r +LEFT JOIN "TrunkEntities" AS t ON r."OptionalReferenceTrunkId" = t."Id" +LEFT JOIN "BranchEntities" AS b ON t."RequiredReferenceBranchId" = b."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_trunk_and_trunk_duplicated(bool async) + { + await base.Select_trunk_and_trunk_duplicated(async); + + AssertSql( + """ +SELECT t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", l."Id", l."CollectionBranchId", l."Name" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +LEFT JOIN "BranchEntities" AS b ON t."OptionalReferenceBranchId" = b."Id" +LEFT JOIN "LeafEntities" AS l ON b."RequiredReferenceLeafId" = l."Id" +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_leaf_trunk_root(bool async) + { + await base.Select_leaf_trunk_root(async); + + AssertSql( + """ +SELECT l."Id", l."CollectionBranchId", l."Name", t."Id", t."CollectionRootId", t."Name", t."OptionalReferenceBranchId", t."RequiredReferenceBranchId", r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId" +FROM "RootEntities" AS r +INNER JOIN "TrunkEntities" AS t ON r."RequiredReferenceTrunkId" = t."Id" +INNER JOIN "BranchEntities" AS b ON t."RequiredReferenceBranchId" = b."Id" +INNER JOIN "LeafEntities" AS l ON b."RequiredReferenceLeafId" = l."Id" +"""); + } + + public override async Task Select_subquery_root_set_required_trunk_FirstOrDefault_branch(bool async) + { + await base.Select_subquery_root_set_required_trunk_FirstOrDefault_branch(async); + + AssertSql( + """ +SELECT s."Id", s."CollectionTrunkId", s."Name", s."OptionalReferenceLeafId", s."RequiredReferenceLeafId" +FROM "RootEntities" AS r +LEFT JOIN LATERAL ( + SELECT b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" + FROM "RootEntities" AS r0 + INNER JOIN "TrunkEntities" AS t ON r0."RequiredReferenceTrunkId" = t."Id" + INNER JOIN "BranchEntities" AS b ON t."RequiredReferenceBranchId" = b."Id" + ORDER BY r0."Id" NULLS FIRST + LIMIT 1 +) AS s ON TRUE +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(bool async) + { + await base.Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(async); + + AssertSql( + """ +SELECT s."Id", s."CollectionTrunkId", s."Name", s."OptionalReferenceLeafId", s."RequiredReferenceLeafId" +FROM "RootEntities" AS r +LEFT JOIN LATERAL ( + SELECT b."Id", b."CollectionTrunkId", b."Name", b."OptionalReferenceLeafId", b."RequiredReferenceLeafId" + FROM "RootEntities" AS r0 + LEFT JOIN "TrunkEntities" AS t ON r0."OptionalReferenceTrunkId" = t."Id" + LEFT JOIN "BranchEntities" AS b ON t."OptionalReferenceBranchId" = b."Id" + ORDER BY r0."Id" NULLS FIRST + LIMIT 1 +) AS s ON TRUE +ORDER BY r."Id" NULLS FIRST +"""); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedJsonNoTrackingProjectionNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedJsonNoTrackingProjectionNpgsqlTest.cs new file mode 100644 index 0000000000..fa38779450 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedJsonNoTrackingProjectionNpgsqlTest.cs @@ -0,0 +1,186 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Projection; + +public class OwnedJsonNoTrackingProjectionNpgsqlTest + : OwnedJsonNoTrackingProjectionRelationalTestBase +{ + public OwnedJsonNoTrackingProjectionNpgsqlTest(OwnedJsonRelationshipsNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Select_trunk_collection(bool async) + { + await base.Select_trunk_collection(async); + + AssertSql( + """ +SELECT r."CollectionTrunk", r."Id" +FROM "RootEntities" AS r +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_required_collection(bool async) + { + await base.Select_branch_required_collection(async); + + AssertSql( + """ +SELECT r."RequiredReferenceTrunk" -> 'CollectionBranch', r."Id" +FROM "RootEntities" AS r +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_optional_collection(bool async) + { + await base.Select_branch_optional_collection(async); + + AssertSql( + """ +SELECT r."RequiredReferenceTrunk" -> 'CollectionBranch', r."Id" +FROM "RootEntities" AS r +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_multiple_branch_leaf(bool async) + { + await base.Select_multiple_branch_leaf(async); + + AssertSql( + """ +SELECT r."Id", r."RequiredReferenceTrunk" -> 'RequiredReferenceBranch', r."RequiredReferenceTrunk" #> '{RequiredReferenceBranch,OptionalReferenceLeaf}', r."RequiredReferenceTrunk" #> '{RequiredReferenceBranch,CollectionLeaf}', r."RequiredReferenceTrunk" -> 'CollectionBranch', r."RequiredReferenceTrunk" #>> '{RequiredReferenceBranch,OptionalReferenceLeaf,Name}' +FROM "RootEntities" AS r +"""); + } + + public override async Task Select_subquery_root_set_trunk_FirstOrDefault_collection(bool async) + { + await base.Select_subquery_root_set_trunk_FirstOrDefault_collection(async); + + AssertSql( + """ +SELECT r1.c, r1."Id", r1.c0 +FROM "RootEntities" AS r +LEFT JOIN LATERAL ( + SELECT r0."RequiredReferenceTrunk" -> 'CollectionBranch' AS c, r0."Id", 1 AS c0 + FROM "RootEntities" AS r0 + ORDER BY r0."Id" NULLS FIRST + LIMIT 1 +) AS r1 ON TRUE +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(bool async) + { + await base.Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(async); + + AssertSql( + """ +SELECT r1.c, r1."Id", r1.c0, r1."Id0", r1.c1, r1.c2, r1.c3, r1.c4 +FROM "RootEntities" AS r +LEFT JOIN LATERAL ( + SELECT r."RequiredReferenceTrunk" -> 'CollectionBranch' AS c, r."Id", r0."RequiredReferenceTrunk" AS c0, r0."Id" AS "Id0", r0."RequiredReferenceTrunk" -> 'RequiredReferenceBranch' AS c1, r0."RequiredReferenceTrunk" ->> 'Name' AS c2, r."RequiredReferenceTrunk" #>> '{RequiredReferenceBranch,Name}' AS c3, 1 AS c4 + FROM "RootEntities" AS r0 + ORDER BY r0."Id" NULLS FIRST + LIMIT 1 +) AS r1 ON TRUE +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(bool async) + { + await base.Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(async); + + AssertSql( + """ +SELECT r1.c, r1."Id", r1.c0 +FROM "RootEntities" AS r +LEFT JOIN LATERAL ( + SELECT r."RequiredReferenceTrunk" -> 'CollectionBranch' AS c, r."Id", 1 AS c0 + FROM "RootEntities" AS r0 + ORDER BY r0."Id" NULLS FIRST + LIMIT 1 +) AS r1 ON TRUE +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task SelectMany_trunk_collection(bool async) + { + await base.SelectMany_trunk_collection(async); + + AssertSql( + """ +SELECT r."Id", c."Name", c."CollectionBranch", c."OptionalReferenceBranch", c."RequiredReferenceBranch" +FROM "RootEntities" AS r +JOIN LATERAL ROWS FROM (jsonb_to_recordset(r."CollectionTrunk") AS ( + "Name" text, + "CollectionBranch" jsonb, + "OptionalReferenceBranch" jsonb, + "RequiredReferenceBranch" jsonb +)) WITH ORDINALITY AS c ON TRUE +"""); + } + + public override async Task SelectMany_required_trunk_reference_branch_collection(bool async) + { + await base.SelectMany_required_trunk_reference_branch_collection(async); + + AssertSql( + """ +SELECT r."Id", c."Name", c."CollectionLeaf", c."OptionalReferenceLeaf", c."RequiredReferenceLeaf" +FROM "RootEntities" AS r +JOIN LATERAL ROWS FROM (jsonb_to_recordset(r."RequiredReferenceTrunk" -> 'CollectionBranch') AS ( + "Name" text, + "CollectionLeaf" jsonb, + "OptionalReferenceLeaf" jsonb, + "RequiredReferenceLeaf" jsonb +)) WITH ORDINALITY AS c ON TRUE +"""); + } + + public override async Task SelectMany_optional_trunk_reference_branch_collection(bool async) + { + await base.SelectMany_optional_trunk_reference_branch_collection(async); + + AssertSql( + """ +SELECT r."Id", c."Name", c."CollectionLeaf", c."OptionalReferenceLeaf", c."RequiredReferenceLeaf" +FROM "RootEntities" AS r +JOIN LATERAL ROWS FROM (jsonb_to_recordset(r."OptionalReferenceTrunk" -> 'CollectionBranch') AS ( + "Name" text, + "CollectionLeaf" jsonb, + "OptionalReferenceLeaf" jsonb, + "RequiredReferenceLeaf" jsonb +)) WITH ORDINALITY AS c ON TRUE +"""); + } + + public override async Task Project_branch_collection_element_using_indexer_constant(bool async) + { + await base.Project_branch_collection_element_using_indexer_constant(async); + + AssertSql( + """ +SELECT r."RequiredReferenceTrunk" -> 'CollectionBranch', r."Id" +FROM "RootEntities" AS r +ORDER BY r."Id" NULLS FIRST +"""); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedJsonProjectionNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedJsonProjectionNpgsqlTest.cs new file mode 100644 index 0000000000..5a33f5a136 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedJsonProjectionNpgsqlTest.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Projection; + +public class OwnedJsonProjectionSqlServerTest + : OwnedJsonProjectionRelationalTestBase +{ + public OwnedJsonProjectionSqlServerTest(OwnedJsonRelationshipsNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override Task Select_trunk_collection(bool async) + => AssertCantTrackJson(() => base.Select_trunk_collection(async)); + + public override Task Select_branch_required_collection(bool async) + => AssertCantTrackJson(() => base.Select_branch_required_collection(async)); + + public override Task Select_branch_optional_collection(bool async) + => AssertCantTrackJson(() => base.Select_branch_optional_collection(async)); + + public override Task Project_branch_collection_element_using_indexer_constant(bool async) + => AssertCantTrackJson(() => base.Project_branch_collection_element_using_indexer_constant(async)); + + public override Task Select_multiple_branch_leaf(bool async) + => AssertCantTrackJson(() => base.Select_multiple_branch_leaf(async)); + + public override Task Select_subquery_root_set_trunk_FirstOrDefault_collection(bool async) + => AssertCantTrackJson(() => base.Select_subquery_root_set_trunk_FirstOrDefault_collection(async)); + + public override Task Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(bool async) + => AssertCantTrackJson(() => base.Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(async)); + + public override Task Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(bool async) + => AssertCantTrackJson(() => base.Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(async)); + + public override Task SelectMany_trunk_collection(bool async) + => AssertCantTrackJson(() => base.SelectMany_trunk_collection(async)); + + public override Task SelectMany_required_trunk_reference_branch_collection(bool async) + => AssertCantTrackJson(() => base.SelectMany_required_trunk_reference_branch_collection(async)); + + public override Task SelectMany_optional_trunk_reference_branch_collection(bool async) + => AssertCantTrackJson(() => base.SelectMany_optional_trunk_reference_branch_collection(async)); + + private async Task AssertCantTrackJson(Func test) + { + var message = (await Assert.ThrowsAsync(test)).Message; + + Assert.Equal(RelationalStrings.JsonEntityOrCollectionProjectedAtRootLevelInTrackingQuery("AsNoTracking"), message); + AssertSql(); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedJsonReferenceNoTrackingProjectionNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedJsonReferenceNoTrackingProjectionNpgsqlTest.cs new file mode 100644 index 0000000000..ac48561018 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedJsonReferenceNoTrackingProjectionNpgsqlTest.cs @@ -0,0 +1,187 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Projection; + +public class OwnedJsonReferenceNoTrackingProjectionNpgsqlTest + : OwnedJsonReferenceNoTrackingProjectionRelationalTestBase +{ + public OwnedJsonReferenceNoTrackingProjectionNpgsqlTest(OwnedJsonRelationshipsNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Select_root(bool async) + { + await base.Select_root(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId", r."CollectionTrunk", r."OptionalReferenceTrunk", r."RequiredReferenceTrunk" +FROM "RootEntities" AS r +"""); + } + + public override async Task Select_trunk_optional(bool async) + { + await base.Select_trunk_optional(async); + + AssertSql( + """ +SELECT r."OptionalReferenceTrunk", r."Id" +FROM "RootEntities" AS r +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_trunk_required(bool async) + { + await base.Select_trunk_required(async); + + AssertSql( + """ +SELECT r."RequiredReferenceTrunk", r."Id" +FROM "RootEntities" AS r +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_required_required(bool async) + { + await base.Select_branch_required_required(async); + + AssertSql( + """ +SELECT r."RequiredReferenceTrunk" -> 'RequiredReferenceBranch', r."Id" +FROM "RootEntities" AS r +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_required_optional(bool async) + { + await base.Select_branch_required_optional(async); + + AssertSql( + """ +SELECT r."RequiredReferenceTrunk" -> 'OptionalReferenceBranch', r."Id" +FROM "RootEntities" AS r +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_optional_required(bool async) + { + await base.Select_branch_optional_required(async); + + AssertSql( + """ +SELECT r."RequiredReferenceTrunk" -> 'RequiredReferenceBranch', r."Id" +FROM "RootEntities" AS r +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_branch_optional_optional(bool async) + { + await base.Select_branch_optional_optional(async); + + AssertSql( + """ +SELECT r."RequiredReferenceTrunk" -> 'OptionalReferenceBranch', r."Id" +FROM "RootEntities" AS r +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_root_duplicated(bool async) + { + await base.Select_root_duplicated(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId", r."CollectionTrunk", r."OptionalReferenceTrunk", r."RequiredReferenceTrunk", r."CollectionTrunk", r."OptionalReferenceTrunk", r."RequiredReferenceTrunk" +FROM "RootEntities" AS r +"""); + } + + public override async Task Select_trunk_and_branch_duplicated(bool async) + { + await base.Select_trunk_and_branch_duplicated(async); + + AssertSql( + """ +SELECT r."OptionalReferenceTrunk", r."Id", r."OptionalReferenceTrunk" -> 'RequiredReferenceBranch', r."OptionalReferenceTrunk", r."OptionalReferenceTrunk" -> 'RequiredReferenceBranch' +FROM "RootEntities" AS r +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_trunk_and_trunk_duplicated(bool async) + { + await base.Select_trunk_and_trunk_duplicated(async); + + AssertSql( + """ +SELECT r."RequiredReferenceTrunk", r."Id", r."RequiredReferenceTrunk" #> '{OptionalReferenceBranch,RequiredReferenceLeaf}', r."RequiredReferenceTrunk", r."RequiredReferenceTrunk" #> '{OptionalReferenceBranch,RequiredReferenceLeaf}' +FROM "RootEntities" AS r +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_leaf_trunk_root(bool async) + { + await base.Select_leaf_trunk_root(async); + + AssertSql( + """ +SELECT r."RequiredReferenceTrunk" #> '{RequiredReferenceBranch,RequiredReferenceLeaf}', r."Id", r."RequiredReferenceTrunk", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId", r."CollectionTrunk", r."OptionalReferenceTrunk", r."RequiredReferenceTrunk" +FROM "RootEntities" AS r +"""); + } + + public override async Task Select_subquery_root_set_required_trunk_FirstOrDefault_branch(bool async) + { + await base.Select_subquery_root_set_required_trunk_FirstOrDefault_branch(async); + + AssertSql( + """ +SELECT r1.c, r1."Id" +FROM "RootEntities" AS r +LEFT JOIN LATERAL ( + SELECT r0."RequiredReferenceTrunk" -> 'RequiredReferenceBranch' AS c, r0."Id" + FROM "RootEntities" AS r0 + ORDER BY r0."Id" NULLS FIRST + LIMIT 1 +) AS r1 ON TRUE +ORDER BY r."Id" NULLS FIRST +"""); + } + + public override async Task Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(bool async) + { + await base.Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(async); + + AssertSql( + """ +SELECT r1.c, r1."Id" +FROM "RootEntities" AS r +LEFT JOIN LATERAL ( + SELECT r0."OptionalReferenceTrunk" -> 'OptionalReferenceBranch' AS c, r0."Id" + FROM "RootEntities" AS r0 + ORDER BY r0."Id" NULLS FIRST + LIMIT 1 +) AS r1 ON TRUE +ORDER BY r."Id" NULLS FIRST +"""); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedJsonReferenceProjectionNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedJsonReferenceProjectionNpgsqlTest.cs new file mode 100644 index 0000000000..7808d7aac4 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedJsonReferenceProjectionNpgsqlTest.cs @@ -0,0 +1,85 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Projection; + +public class OwnedJsonReferenceProjectionSqlServerTest + : OwnedJsonReferenceProjectionRelationalTestBase +{ + public OwnedJsonReferenceProjectionSqlServerTest(OwnedJsonRelationshipsNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Select_root(bool async) + { + await base.Select_root(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId", r."CollectionTrunk", r."OptionalReferenceTrunk", r."RequiredReferenceTrunk" +FROM "RootEntities" AS r +"""); + } + + public override async Task Select_root_duplicated(bool async) + { + await base.Select_root_duplicated(async); + + AssertSql( + """ +SELECT r."Id", r."Name", r."OptionalReferenceTrunkId", r."RequiredReferenceTrunkId", r."CollectionTrunk", r."OptionalReferenceTrunk", r."RequiredReferenceTrunk", r."CollectionTrunk", r."OptionalReferenceTrunk", r."RequiredReferenceTrunk" +FROM "RootEntities" AS r +"""); + } + + public override Task Select_trunk_optional(bool async) + => AssertCantTrackJson(() => base.Select_trunk_optional(async)); + + public override Task Select_trunk_required(bool async) + => AssertCantTrackJson(() => base.Select_trunk_required(async)); + + public override Task Select_branch_required_required(bool async) + => AssertCantTrackJson(() => base.Select_branch_required_required(async)); + + public override Task Select_branch_required_optional(bool async) + => AssertCantTrackJson(() => base.Select_branch_required_optional(async)); + + public override Task Select_branch_optional_required(bool async) + => AssertCantTrackJson(() => base.Select_branch_optional_required(async)); + + public override Task Select_branch_optional_optional(bool async) + => AssertCantTrackJson(() => base.Select_branch_optional_optional(async)); + + public override Task Select_trunk_and_branch_duplicated(bool async) + => AssertCantTrackJson(() => base.Select_trunk_and_branch_duplicated(async)); + + public override Task Select_trunk_and_trunk_duplicated(bool async) + => AssertCantTrackJson(() => base.Select_trunk_and_trunk_duplicated(async)); + + public override Task Select_leaf_trunk_root(bool async) + => AssertCantTrackJson(() => base.Select_leaf_trunk_root(async)); + + public override Task Select_subquery_root_set_required_trunk_FirstOrDefault_branch(bool async) + => AssertCantTrackJson(() => base.Select_subquery_root_set_required_trunk_FirstOrDefault_branch(async)); + + public override Task Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(bool async) + => AssertCantTrackJson(() => base.Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(async)); + + private async Task AssertCantTrackJson(Func test) + { + var message = (await Assert.ThrowsAsync(test)).Message; + + Assert.Equal(RelationalStrings.JsonEntityOrCollectionProjectedAtRootLevelInTrackingQuery("AsNoTracking"), message); + AssertSql(); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedNoTrackingProjectionNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedNoTrackingProjectionNpgsqlTest.cs new file mode 100644 index 0000000000..f40535886f --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedNoTrackingProjectionNpgsqlTest.cs @@ -0,0 +1,195 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit.Sdk; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Projection; + +public class OwnedNoTrackingProjectionNpgsqlTest + : OwnedNoTrackingProjectionRelationalTestBase +{ + public OwnedNoTrackingProjectionNpgsqlTest(OwnedRelationshipsNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Select_trunk_collection(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_trunk_collection(async)); + + Assert.Equal("42702", exception.SqlState); + } + + public override async Task Select_branch_required_collection(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.Select_branch_required_collection(async)); + + AssertSql( + """ +SELECT r."Id", s."RelationshipsTrunkEntityRelationshipsRootEntityId", s."Id1", s."Name", s."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", s."RelationshipsBranchEntityId1", s."Id10", s."Name0", s."OptionalReferenceLeaf_Name", s."RequiredReferenceLeaf_Name" +FROM "RootEntities" AS r +LEFT JOIN ( + SELECT r0."RelationshipsTrunkEntityRelationshipsRootEntityId", r0."Id1", r0."Name", r1."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r1."RelationshipsBranchEntityId1", r1."Id1" AS "Id10", r1."Name" AS "Name0", r0."OptionalReferenceLeaf_Name", r0."RequiredReferenceLeaf_Name" + FROM "Root_RequiredReferenceTrunk_CollectionBranch" AS r0 + LEFT JOIN "Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf" AS r1 ON r0."RelationshipsTrunkEntityRelationshipsRootEntityId" = r1."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" AND r0."Id1" = r1."RelationshipsBranchEntityId1" +) AS s ON r."Id" = s."RelationshipsTrunkEntityRelationshipsRootEntityId" +ORDER BY r."Id" NULLS FIRST, s."RelationshipsTrunkEntityRelationshipsRootEntityId" NULLS FIRST, s."Id1" NULLS FIRST, s."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, s."RelationshipsBranchEntityId1" NULLS FIRST +"""); + } + + public override async Task Select_branch_optional_collection(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.Select_branch_optional_collection(async)); + + AssertSql( + """ +SELECT r."Id", s."RelationshipsTrunkEntityRelationshipsRootEntityId", s."Id1", s."Name", s."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", s."RelationshipsBranchEntityId1", s."Id10", s."Name0", s."OptionalReferenceLeaf_Name", s."RequiredReferenceLeaf_Name" +FROM "RootEntities" AS r +LEFT JOIN ( + SELECT r0."RelationshipsTrunkEntityRelationshipsRootEntityId", r0."Id1", r0."Name", r1."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r1."RelationshipsBranchEntityId1", r1."Id1" AS "Id10", r1."Name" AS "Name0", r0."OptionalReferenceLeaf_Name", r0."RequiredReferenceLeaf_Name" + FROM "Root_RequiredReferenceTrunk_CollectionBranch" AS r0 + LEFT JOIN "Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf" AS r1 ON r0."RelationshipsTrunkEntityRelationshipsRootEntityId" = r1."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" AND r0."Id1" = r1."RelationshipsBranchEntityId1" +) AS s ON r."Id" = s."RelationshipsTrunkEntityRelationshipsRootEntityId" +ORDER BY r."Id" NULLS FIRST, s."RelationshipsTrunkEntityRelationshipsRootEntityId" NULLS FIRST, s."Id1" NULLS FIRST, s."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, s."RelationshipsBranchEntityId1" NULLS FIRST +"""); + } + + public override async Task Select_multiple_branch_leaf(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.Select_multiple_branch_leaf(async)); + + AssertSql( + """ +SELECT r."Id", r."RequiredReferenceTrunk_RequiredReferenceBranch_Name", r0."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r0."Id1", r0."Name", r."RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferen~", r."RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferen~", r1."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r1."Id1", r1."Name", s."RelationshipsTrunkEntityRelationshipsRootEntityId", s."Id1", s."Name", s."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", s."RelationshipsBranchEntityId1", s."Id10", s."Name0", s."OptionalReferenceLeaf_Name", s."RequiredReferenceLeaf_Name" +FROM "RootEntities" AS r +LEFT JOIN "Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf" AS r0 ON r."Id" = r0."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +LEFT JOIN "Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf" AS r1 ON r."Id" = r1."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +LEFT JOIN ( + SELECT r2."RelationshipsTrunkEntityRelationshipsRootEntityId", r2."Id1", r2."Name", r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r3."RelationshipsBranchEntityId1", r3."Id1" AS "Id10", r3."Name" AS "Name0", r2."OptionalReferenceLeaf_Name", r2."RequiredReferenceLeaf_Name" + FROM "Root_RequiredReferenceTrunk_CollectionBranch" AS r2 + LEFT JOIN "Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf" AS r3 ON r2."RelationshipsTrunkEntityRelationshipsRootEntityId" = r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" AND r2."Id1" = r3."RelationshipsBranchEntityId1" +) AS s ON r."Id" = s."RelationshipsTrunkEntityRelationshipsRootEntityId" +ORDER BY r."Id" NULLS FIRST, r0."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r0."Id1" NULLS FIRST, r1."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r1."Id1" NULLS FIRST, s."RelationshipsTrunkEntityRelationshipsRootEntityId" NULLS FIRST, s."Id1" NULLS FIRST, s."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, s."RelationshipsBranchEntityId1" NULLS FIRST +"""); + } + + public override async Task Select_subquery_root_set_trunk_FirstOrDefault_collection(bool async) + { + await base.Select_subquery_root_set_trunk_FirstOrDefault_collection(async); + + AssertSql( + """ +SELECT r."Id", r3."Id", s."RelationshipsTrunkEntityRelationshipsRootEntityId", s."Id1", s."Name", s."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", s."RelationshipsBranchEntityId1", s."Id10", s."Name0", s."OptionalReferenceLeaf_Name", s."RequiredReferenceLeaf_Name", r3.c +FROM "RootEntities" AS r +LEFT JOIN LATERAL ( + SELECT 1 AS c, r0."Id" + FROM "RootEntities" AS r0 + ORDER BY r0."Id" NULLS FIRST + LIMIT 1 +) AS r3 ON TRUE +LEFT JOIN ( + SELECT r1."RelationshipsTrunkEntityRelationshipsRootEntityId", r1."Id1", r1."Name", r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r2."RelationshipsBranchEntityId1", r2."Id1" AS "Id10", r2."Name" AS "Name0", r1."OptionalReferenceLeaf_Name", r1."RequiredReferenceLeaf_Name" + FROM "Root_RequiredReferenceTrunk_CollectionBranch" AS r1 + LEFT JOIN "Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf" AS r2 ON r1."RelationshipsTrunkEntityRelationshipsRootEntityId" = r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" AND r1."Id1" = r2."RelationshipsBranchEntityId1" +) AS s ON r3."Id" = s."RelationshipsTrunkEntityRelationshipsRootEntityId" +ORDER BY r."Id" NULLS FIRST, r3."Id" NULLS FIRST, s."RelationshipsTrunkEntityRelationshipsRootEntityId" NULLS FIRST, s."Id1" NULLS FIRST, s."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, s."RelationshipsBranchEntityId1" NULLS FIRST +"""); + } + + // https://github.com/dotnet/efcore/pull/35942 + public override Task Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(bool async) + => Task.CompletedTask; + + public override async Task Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(async)); + + AssertSql( + """ +SELECT r."Id", r3."Id", s."RelationshipsTrunkEntityRelationshipsRootEntityId", s."Id1", s."Name", s."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", s."RelationshipsBranchEntityId1", s."Id10", s."Name0", s."OptionalReferenceLeaf_Name", s."RequiredReferenceLeaf_Name", r3.c +FROM "RootEntities" AS r +LEFT JOIN LATERAL ( + SELECT 1 AS c, r0."Id" + FROM "RootEntities" AS r0 + ORDER BY r0."Id" NULLS FIRST + LIMIT 1 +) AS r3 ON TRUE +LEFT JOIN ( + SELECT r1."RelationshipsTrunkEntityRelationshipsRootEntityId", r1."Id1", r1."Name", r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r2."RelationshipsBranchEntityId1", r2."Id1" AS "Id10", r2."Name" AS "Name0", r1."OptionalReferenceLeaf_Name", r1."RequiredReferenceLeaf_Name" + FROM "Root_RequiredReferenceTrunk_CollectionBranch" AS r1 + LEFT JOIN "Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf" AS r2 ON r1."RelationshipsTrunkEntityRelationshipsRootEntityId" = r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" AND r1."Id1" = r2."RelationshipsBranchEntityId1" +) AS s ON r."Id" = s."RelationshipsTrunkEntityRelationshipsRootEntityId" +ORDER BY r."Id" NULLS FIRST, r3."Id" NULLS FIRST, s."RelationshipsTrunkEntityRelationshipsRootEntityId" NULLS FIRST, s."Id1" NULLS FIRST, s."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, s."RelationshipsBranchEntityId1" NULLS FIRST +"""); + } + + public override async Task SelectMany_trunk_collection(bool async) + { + await base.SelectMany_trunk_collection(async); + + AssertSql( + """ +SELECT r0."RelationshipsRootEntityId", r0."Id1", r0."Name", r."Id", s."RelationshipsTrunkEntityRelationshipsRootEntityId", s."RelationshipsTrunkEntityId1", s."Id1", s."Name", s."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", s."RelationshipsBranchEntityRelationshipsTrunkEntityId1", s."RelationshipsBranchEntityId1", s."Id10", s."Name0", s."OptionalReferenceLeaf_Name", s."RequiredReferenceLeaf_Name", r0."OptionalReferenceBranch_Name", r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r3."RelationshipsBranchEntityRelationshipsTrunkEntityId1", r3."Id1", r3."Name", r0."OptionalReferenceBranch_OptionalReferenceLeaf_Name", r0."OptionalReferenceBranch_RequiredReferenceLeaf_Name", r0."RequiredReferenceBranch_Name", r4."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r4."RelationshipsBranchEntityRelationshipsTrunkEntityId1", r4."Id1", r4."Name", r0."RequiredReferenceBranch_OptionalReferenceLeaf_Name", r0."RequiredReferenceBranch_RequiredReferenceLeaf_Name" +FROM "RootEntities" AS r +INNER JOIN "Root_CollectionTrunk" AS r0 ON r."Id" = r0."RelationshipsRootEntityId" +LEFT JOIN ( + SELECT r1."RelationshipsTrunkEntityRelationshipsRootEntityId", r1."RelationshipsTrunkEntityId1", r1."Id1", r1."Name", r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r2."RelationshipsBranchEntityRelationshipsTrunkEntityId1", r2."RelationshipsBranchEntityId1", r2."Id1" AS "Id10", r2."Name" AS "Name0", r1."OptionalReferenceLeaf_Name", r1."RequiredReferenceLeaf_Name" + FROM "Root_CollectionTrunk_CollectionBranch" AS r1 + LEFT JOIN "Root_CollectionTrunk_CollectionBranch_CollectionLeaf" AS r2 ON r1."RelationshipsTrunkEntityRelationshipsRootEntityId" = r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" AND r1."RelationshipsTrunkEntityId1" = r2."RelationshipsBranchEntityRelationshipsTrunkEntityId1" AND r1."Id1" = r2."RelationshipsBranchEntityId1" +) AS s ON r0."RelationshipsRootEntityId" = s."RelationshipsTrunkEntityRelationshipsRootEntityId" AND r0."Id1" = s."RelationshipsTrunkEntityId1" +LEFT JOIN "Root_CollectionTrunk_OptionalReferenceBranch_CollectionLeaf" AS r3 ON CASE + WHEN r0."OptionalReferenceBranch_Name" IS NOT NULL THEN r0."RelationshipsRootEntityId" +END = r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" AND CASE + WHEN r0."OptionalReferenceBranch_Name" IS NOT NULL THEN r0."Id1" +END = r3."RelationshipsBranchEntityRelationshipsTrunkEntityId1" +LEFT JOIN "Root_CollectionTrunk_RequiredReferenceBranch_CollectionLeaf" AS r4 ON r0."RelationshipsRootEntityId" = r4."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" AND r0."Id1" = r4."RelationshipsBranchEntityRelationshipsTrunkEntityId1" +ORDER BY r."Id" NULLS FIRST, r0."RelationshipsRootEntityId" NULLS FIRST, r0."Id1" NULLS FIRST, s."RelationshipsTrunkEntityRelationshipsRootEntityId" NULLS FIRST, s."RelationshipsTrunkEntityId1" NULLS FIRST, s."Id1" NULLS FIRST, s."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, s."RelationshipsBranchEntityRelationshipsTrunkEntityId1" NULLS FIRST, s."RelationshipsBranchEntityId1" NULLS FIRST, s."Id10" NULLS FIRST, r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r3."RelationshipsBranchEntityRelationshipsTrunkEntityId1" NULLS FIRST, r3."Id1" NULLS FIRST, r4."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r4."RelationshipsBranchEntityRelationshipsTrunkEntityId1" NULLS FIRST +"""); + } + + public override async Task SelectMany_required_trunk_reference_branch_collection(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.SelectMany_required_trunk_reference_branch_collection(async)); + + AssertSql( + """ +SELECT r0."RelationshipsTrunkEntityRelationshipsRootEntityId", r0."Id1", r0."Name", r."Id", r1."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r1."RelationshipsBranchEntityId1", r1."Id1", r1."Name", r0."OptionalReferenceLeaf_Name", r0."RequiredReferenceLeaf_Name" +FROM "RootEntities" AS r +INNER JOIN "Root_RequiredReferenceTrunk_CollectionBranch" AS r0 ON r."Id" = r0."RelationshipsTrunkEntityRelationshipsRootEntityId" +LEFT JOIN "Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf" AS r1 ON r0."RelationshipsTrunkEntityRelationshipsRootEntityId" = r1."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" AND r0."Id1" = r1."RelationshipsBranchEntityId1" +ORDER BY r."Id" NULLS FIRST, r0."RelationshipsTrunkEntityRelationshipsRootEntityId" NULLS FIRST, r0."Id1" NULLS FIRST, r1."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r1."RelationshipsBranchEntityId1" NULLS FIRST +"""); + } + + public override async Task SelectMany_optional_trunk_reference_branch_collection(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.SelectMany_optional_trunk_reference_branch_collection(async)); + + AssertSql( + """ +SELECT r0."RelationshipsTrunkEntityRelationshipsRootEntityId", r0."Id1", r0."Name", r."Id", r1."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r1."RelationshipsBranchEntityId1", r1."Id1", r1."Name", r0."OptionalReferenceLeaf_Name", r0."RequiredReferenceLeaf_Name" +FROM "RootEntities" AS r +INNER JOIN "Root_OptionalReferenceTrunk_CollectionBranch" AS r0 ON CASE + WHEN r."OptionalReferenceTrunk_Name" IS NOT NULL THEN r."Id" +END = r0."RelationshipsTrunkEntityRelationshipsRootEntityId" +LEFT JOIN "Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf" AS r1 ON r0."RelationshipsTrunkEntityRelationshipsRootEntityId" = r1."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" AND r0."Id1" = r1."RelationshipsBranchEntityId1" +ORDER BY r."Id" NULLS FIRST, r0."RelationshipsTrunkEntityRelationshipsRootEntityId" NULLS FIRST, r0."Id1" NULLS FIRST, r1."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r1."RelationshipsBranchEntityId1" NULLS FIRST +"""); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedProjectionNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedProjectionNpgsqlTest.cs new file mode 100644 index 0000000000..1ab86ca3cf --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedProjectionNpgsqlTest.cs @@ -0,0 +1,57 @@ +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Projection; + +public class OwnedProjectionNpgsqlTest + : OwnedProjectionRelationalTestBase +{ + public OwnedProjectionNpgsqlTest(OwnedRelationshipsNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override Task Select_trunk_collection(bool async) + => AssertCantTrackOwned(() => base.Select_trunk_collection(async)); + + public override Task Select_branch_required_collection(bool async) + => AssertCantTrackOwned(() => base.Select_branch_required_collection(async)); + + public override Task Select_branch_optional_collection(bool async) + => AssertCantTrackOwned(() => base.Select_branch_optional_collection(async)); + + public override Task Select_multiple_branch_leaf(bool async) + => AssertCantTrackOwned(() => base.Select_multiple_branch_leaf(async)); + + public override Task Select_subquery_root_set_trunk_FirstOrDefault_collection(bool async) + => AssertCantTrackOwned(() => base.Select_subquery_root_set_trunk_FirstOrDefault_collection(async)); + + public override Task Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(bool async) + => AssertCantTrackOwned(() => base.Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(async)); + + public override Task Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(bool async) + => AssertCantTrackOwned(() => base.Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(async)); + + public override Task SelectMany_trunk_collection(bool async) + => AssertCantTrackOwned(() => base.SelectMany_trunk_collection(async)); + + public override Task SelectMany_required_trunk_reference_branch_collection(bool async) + => AssertCantTrackOwned(() => base.SelectMany_required_trunk_reference_branch_collection(async)); + + public override Task SelectMany_optional_trunk_reference_branch_collection(bool async) + => AssertCantTrackOwned(() => base.SelectMany_optional_trunk_reference_branch_collection(async)); + + private async Task AssertCantTrackOwned(Func test) + { + var message = (await Assert.ThrowsAsync(test)).Message; + + Assert.Equal(CoreStrings.OwnedEntitiesCannotBeTrackedWithoutTheirOwner, message); + AssertSql(); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedReferenceNoTrackingProjectionNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedReferenceNoTrackingProjectionNpgsqlTest.cs new file mode 100644 index 0000000000..e0d458d700 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedReferenceNoTrackingProjectionNpgsqlTest.cs @@ -0,0 +1,187 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit.Sdk; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Projection; + +public class OwnedReferenceNoTrackingProjectionNpgsqlTest + : OwnedReferenceNoTrackingProjectionRelationalTestBase +{ + public OwnedReferenceNoTrackingProjectionNpgsqlTest(OwnedRelationshipsNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Select_root(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_root(async)); + + Assert.Equal("42702", exception.SqlState); + } + + public override async Task Select_trunk_optional(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.Select_trunk_optional(async)); + + AssertSql( + """ +SELECT r."Id", r."OptionalReferenceTrunk_Name", s."RelationshipsTrunkEntityRelationshipsRootEntityId", s."Id1", s."Name", s."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", s."RelationshipsBranchEntityId1", s."Id10", s."Name0", s."OptionalReferenceLeaf_Name", s."RequiredReferenceLeaf_Name", r."OptionalReferenceTrunk_OptionalReferenceBranch_Name", r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r2."Id1", r2."Name", r."OptionalReferenceTrunk_OptionalReferenceBranch_OptionalReferen~", r."OptionalReferenceTrunk_OptionalReferenceBranch_RequiredReferen~", r."OptionalReferenceTrunk_RequiredReferenceBranch_Name", r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r3."Id1", r3."Name", r."OptionalReferenceTrunk_RequiredReferenceBranch_OptionalReferen~", r."OptionalReferenceTrunk_RequiredReferenceBranch_RequiredReferen~" +FROM "RootEntities" AS r +LEFT JOIN ( + SELECT r0."RelationshipsTrunkEntityRelationshipsRootEntityId", r0."Id1", r0."Name", r1."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r1."RelationshipsBranchEntityId1", r1."Id1" AS "Id10", r1."Name" AS "Name0", r0."OptionalReferenceLeaf_Name", r0."RequiredReferenceLeaf_Name" + FROM "Root_OptionalReferenceTrunk_CollectionBranch" AS r0 + LEFT JOIN "Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf" AS r1 ON r0."RelationshipsTrunkEntityRelationshipsRootEntityId" = r1."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" AND r0."Id1" = r1."RelationshipsBranchEntityId1" +) AS s ON CASE + WHEN r."OptionalReferenceTrunk_Name" IS NOT NULL THEN r."Id" +END = s."RelationshipsTrunkEntityRelationshipsRootEntityId" +LEFT JOIN "Root_OptionalReferenceTrunk_OptionalReferenceBranch_CollectionLeaf" AS r2 ON CASE + WHEN r."OptionalReferenceTrunk_OptionalReferenceBranch_Name" IS NOT NULL THEN r."Id" +END = r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +LEFT JOIN "Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf" AS r3 ON CASE + WHEN r."OptionalReferenceTrunk_RequiredReferenceBranch_Name" IS NOT NULL THEN r."Id" +END = r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +ORDER BY r."Id" NULLS FIRST, s."RelationshipsTrunkEntityRelationshipsRootEntityId" NULLS FIRST, s."Id1" NULLS FIRST, s."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, s."RelationshipsBranchEntityId1" NULLS FIRST, s."Id10" NULLS FIRST, r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r2."Id1" NULLS FIRST, r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST +"""); + } + + public override async Task Select_trunk_required(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.Select_trunk_required(async)); + + AssertSql( + """ +SELECT r."Id", r."RequiredReferenceTrunk_Name", s."RelationshipsTrunkEntityRelationshipsRootEntityId", s."Id1", s."Name", s."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", s."RelationshipsBranchEntityId1", s."Id10", s."Name0", s."OptionalReferenceLeaf_Name", s."RequiredReferenceLeaf_Name", r."RequiredReferenceTrunk_OptionalReferenceBranch_Name", r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r2."Id1", r2."Name", r."RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferen~", r."RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferen~", r."RequiredReferenceTrunk_RequiredReferenceBranch_Name", r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r3."Id1", r3."Name", r."RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferen~", r."RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferen~" +FROM "RootEntities" AS r +LEFT JOIN ( + SELECT r0."RelationshipsTrunkEntityRelationshipsRootEntityId", r0."Id1", r0."Name", r1."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r1."RelationshipsBranchEntityId1", r1."Id1" AS "Id10", r1."Name" AS "Name0", r0."OptionalReferenceLeaf_Name", r0."RequiredReferenceLeaf_Name" + FROM "Root_RequiredReferenceTrunk_CollectionBranch" AS r0 + LEFT JOIN "Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf" AS r1 ON r0."RelationshipsTrunkEntityRelationshipsRootEntityId" = r1."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" AND r0."Id1" = r1."RelationshipsBranchEntityId1" +) AS s ON r."Id" = s."RelationshipsTrunkEntityRelationshipsRootEntityId" +LEFT JOIN "Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf" AS r2 ON CASE + WHEN r."RequiredReferenceTrunk_OptionalReferenceBranch_Name" IS NOT NULL THEN r."Id" +END = r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +LEFT JOIN "Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf" AS r3 ON r."Id" = r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +ORDER BY r."Id" NULLS FIRST, s."RelationshipsTrunkEntityRelationshipsRootEntityId" NULLS FIRST, s."Id1" NULLS FIRST, s."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, s."RelationshipsBranchEntityId1" NULLS FIRST, s."Id10" NULLS FIRST, r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r2."Id1" NULLS FIRST, r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST +"""); + } + + public override async Task Select_branch_required_required(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.Select_branch_required_required(async)); + + AssertSql( + """ +SELECT r."Id", r."RequiredReferenceTrunk_RequiredReferenceBranch_Name", r0."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r0."Id1", r0."Name", r."RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferen~", r."RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferen~" +FROM "RootEntities" AS r +LEFT JOIN "Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf" AS r0 ON r."Id" = r0."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +ORDER BY r."Id" NULLS FIRST, r0."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST +"""); + } + + public override async Task Select_branch_required_optional(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.Select_branch_required_optional(async)); + + AssertSql( + """ +SELECT r."Id", r."RequiredReferenceTrunk_OptionalReferenceBranch_Name", r0."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r0."Id1", r0."Name", r."RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferen~", r."RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferen~" +FROM "RootEntities" AS r +LEFT JOIN "Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf" AS r0 ON CASE + WHEN r."RequiredReferenceTrunk_OptionalReferenceBranch_Name" IS NOT NULL THEN r."Id" +END = r0."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +ORDER BY r."Id" NULLS FIRST, r0."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST +"""); + } + + public override async Task Select_branch_optional_required(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.Select_branch_optional_required(async)); + + AssertSql( + """ +SELECT r."Id", r."RequiredReferenceTrunk_RequiredReferenceBranch_Name", r0."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r0."Id1", r0."Name", r."RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferen~", r."RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferen~" +FROM "RootEntities" AS r +LEFT JOIN "Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf" AS r0 ON r."Id" = r0."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +ORDER BY r."Id" NULLS FIRST, r0."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST +"""); + } + + public override async Task Select_branch_optional_optional(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.Select_branch_optional_optional(async)); + + AssertSql( + """ +SELECT r."Id", r."RequiredReferenceTrunk_OptionalReferenceBranch_Name", r0."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r0."Id1", r0."Name", r."RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferen~", r."RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferen~" +FROM "RootEntities" AS r +LEFT JOIN "Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf" AS r0 ON CASE + WHEN r."RequiredReferenceTrunk_OptionalReferenceBranch_Name" IS NOT NULL THEN r."Id" +END = r0."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +ORDER BY r."Id" NULLS FIRST, r0."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST +"""); + } + + public override async Task Select_root_duplicated(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_root(async)); + + Assert.Equal("42702", exception.SqlState); + } + + // https://github.com/dotnet/efcore/issues/26993 + public override Task Select_trunk_and_branch_duplicated(bool async) + => Task.CompletedTask; + + // https://github.com/dotnet/efcore/issues/26993 + public override Task Select_trunk_and_trunk_duplicated(bool async) + => Task.CompletedTask; + + public override async Task Select_leaf_trunk_root(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_leaf_trunk_root(async)); + + Assert.Equal("42702", exception.SqlState); + } + + public override async Task Select_subquery_root_set_required_trunk_FirstOrDefault_branch(bool async) + { + await base.Select_subquery_root_set_required_trunk_FirstOrDefault_branch(async); + + AssertSql( + """ +SELECT r2."Id", r2."RequiredReferenceTrunk_RequiredReferenceBranch_Name", r."Id", r1."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r1."Id1", r1."Name", r2."RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferen~", r2."RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferen~" +FROM "RootEntities" AS r +LEFT JOIN LATERAL ( + SELECT r0."Id", r0."RequiredReferenceTrunk_RequiredReferenceBranch_Name", r0."RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferen~", r0."RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferen~" + FROM "RootEntities" AS r0 + ORDER BY r0."Id" NULLS FIRST + LIMIT 1 +) AS r2 ON TRUE +LEFT JOIN "Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf" AS r1 ON r2."Id" = r1."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +ORDER BY r."Id" NULLS FIRST, r2."Id" NULLS FIRST, r1."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST +"""); + } + + // https://github.com/dotnet/efcore/issues/26993 + public override Task Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(bool async) + => Task.CompletedTask; + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedReferenceProjectionNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedReferenceProjectionNpgsqlTest.cs new file mode 100644 index 0000000000..a0416f6433 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedReferenceProjectionNpgsqlTest.cs @@ -0,0 +1,79 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Projection; + +public class OwnedReferenceProjectionNpgsqlTest + : OwnedReferenceProjectionRelationalTestBase +{ + public OwnedReferenceProjectionNpgsqlTest(OwnedRelationshipsNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Select_root(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_root(async)); + + Assert.Equal("42702", exception.SqlState); + } + + public override async Task Select_root_duplicated(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_root_duplicated(async)); + + Assert.Equal("42702", exception.SqlState); + } + + public override Task Select_trunk_optional(bool async) + => AssertCantTrackOwned(() => base.Select_trunk_optional(async)); + + public override Task Select_trunk_required(bool async) + => AssertCantTrackOwned(() => base.Select_trunk_required(async)); + + public override Task Select_branch_required_required(bool async) + => AssertCantTrackOwned(() => base.Select_branch_required_required(async)); + + public override Task Select_branch_required_optional(bool async) + => AssertCantTrackOwned(() => base.Select_branch_required_optional(async)); + + public override Task Select_branch_optional_required(bool async) + => AssertCantTrackOwned(() => base.Select_branch_optional_required(async)); + + public override Task Select_branch_optional_optional(bool async) + => AssertCantTrackOwned(() => base.Select_branch_optional_optional(async)); + + public override Task Select_trunk_and_branch_duplicated(bool async) + => AssertCantTrackOwned(() => base.Select_trunk_and_branch_duplicated(async)); + + public override Task Select_trunk_and_trunk_duplicated(bool async) + => AssertCantTrackOwned(() => base.Select_trunk_and_trunk_duplicated(async)); + + public override Task Select_leaf_trunk_root(bool async) + => AssertCantTrackOwned(() => base.Select_leaf_trunk_root(async)); + + public override Task Select_subquery_root_set_required_trunk_FirstOrDefault_branch(bool async) + => AssertCantTrackOwned(() => base.Select_subquery_root_set_required_trunk_FirstOrDefault_branch(async)); + + public override Task Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(bool async) + => AssertCantTrackOwned(() => base.Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(async)); + + private async Task AssertCantTrackOwned(Func test) + { + var message = (await Assert.ThrowsAsync(test)).Message; + + Assert.Equal(CoreStrings.OwnedEntitiesCannotBeTrackedWithoutTheirOwner, message); + AssertSql(); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedTableSplittingNoTrackingProjectionNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedTableSplittingNoTrackingProjectionNpgsqlTest.cs new file mode 100644 index 0000000000..a62de6b3af --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedTableSplittingNoTrackingProjectionNpgsqlTest.cs @@ -0,0 +1,124 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit.Sdk; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Projection; + +public class OwnedTableSplittingNoTrackingProjectionNpgsqlTest + : OwnedTableSplittingNoTrackingProjectionRelationalTestBase +{ + public OwnedTableSplittingNoTrackingProjectionNpgsqlTest(OwnedTableSplittingRelationshipsNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Select_trunk_collection(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_trunk_collection(async)); + + Assert.Equal("42702", exception.SqlState); + } + + public override async Task Select_branch_required_collection(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_branch_required_collection(async)); + + Assert.Equal("42702", exception.SqlState); + } + + public override async Task Select_branch_optional_collection(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_branch_optional_collection(async)); + + Assert.Equal("42702", exception.SqlState); + } + + public override async Task Select_multiple_branch_leaf(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_multiple_branch_leaf(async)); + + Assert.Equal("42702", exception.SqlState); + } + + public override async Task Select_subquery_root_set_trunk_FirstOrDefault_collection(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_subquery_root_set_trunk_FirstOrDefault_collection(async)); + + Assert.Equal("42702", exception.SqlState); + } + + public override async Task Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(async)); + + Assert.Equal("42702", exception.SqlState); + } + + public override async Task Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(async)); + + Assert.Equal("42702", exception.SqlState); + } + + public override async Task SelectMany_trunk_collection(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.SelectMany_trunk_collection(async)); + + Assert.Equal("42702", exception.SqlState); + } + + public override async Task SelectMany_required_trunk_reference_branch_collection(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.SelectMany_required_trunk_reference_branch_collection(async)); + + AssertSql( + """ +SELECT r1."RelationshipsTrunkEntityRelationshipsRootEntityId", r1."Id1", r1."Name", r."Id", r0."RelationshipsRootEntityId", r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r2."RelationshipsBranchEntityId1", r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r3."RelationshipsBranchEntityId1", r4."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r4."RelationshipsBranchEntityId1", r4."Id1", r4."Name", r2."Name", r3."Name" +FROM "RootEntities" AS r +LEFT JOIN "Root_RequiredReferenceTrunk" AS r0 ON r."Id" = r0."RelationshipsRootEntityId" +INNER JOIN "Root_RequiredReferenceTrunk_CollectionBranch" AS r1 ON r0."RelationshipsRootEntityId" = r1."RelationshipsTrunkEntityRelationshipsRootEntityId" +LEFT JOIN "Root_RequiredReferenceTrunk_CollectionBranch_OptionalReferenceLeaf" AS r2 ON r1."RelationshipsTrunkEntityRelationshipsRootEntityId" = r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" AND r1."Id1" = r2."RelationshipsBranchEntityId1" +LEFT JOIN "Root_RequiredReferenceTrunk_CollectionBranch_RequiredReferenceLeaf" AS r3 ON r1."RelationshipsTrunkEntityRelationshipsRootEntityId" = r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" AND r1."Id1" = r3."RelationshipsBranchEntityId1" +LEFT JOIN "Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf" AS r4 ON r1."RelationshipsTrunkEntityRelationshipsRootEntityId" = r4."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" AND r1."Id1" = r4."RelationshipsBranchEntityId1" +ORDER BY r."Id" NULLS FIRST, r0."RelationshipsRootEntityId" NULLS FIRST, r1."RelationshipsTrunkEntityRelationshipsRootEntityId" NULLS FIRST, r1."Id1" NULLS FIRST, r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r2."RelationshipsBranchEntityId1" NULLS FIRST, r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r3."RelationshipsBranchEntityId1" NULLS FIRST, r4."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r4."RelationshipsBranchEntityId1" NULLS FIRST +"""); + } + + public override async Task SelectMany_optional_trunk_reference_branch_collection(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.SelectMany_optional_trunk_reference_branch_collection(async)); + + AssertSql( + """ +SELECT r1."RelationshipsTrunkEntityRelationshipsRootEntityId", r1."Id1", r1."Name", r."Id", r0."RelationshipsRootEntityId", r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r2."RelationshipsBranchEntityId1", r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r3."RelationshipsBranchEntityId1", r4."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r4."RelationshipsBranchEntityId1", r4."Id1", r4."Name", r2."Name", r3."Name" +FROM "RootEntities" AS r +LEFT JOIN "Root_OptionalReferenceTrunk" AS r0 ON r."Id" = r0."RelationshipsRootEntityId" +INNER JOIN "Root_OptionalReferenceTrunk_CollectionBranch" AS r1 ON r0."RelationshipsRootEntityId" = r1."RelationshipsTrunkEntityRelationshipsRootEntityId" +LEFT JOIN "Root_OptionalReferenceTrunk_CollectionBranch_OptionalReferenceLeaf" AS r2 ON r1."RelationshipsTrunkEntityRelationshipsRootEntityId" = r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" AND r1."Id1" = r2."RelationshipsBranchEntityId1" +LEFT JOIN "Root_OptionalReferenceTrunk_CollectionBranch_RequiredReferenceLeaf" AS r3 ON r1."RelationshipsTrunkEntityRelationshipsRootEntityId" = r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" AND r1."Id1" = r3."RelationshipsBranchEntityId1" +LEFT JOIN "Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf" AS r4 ON r1."RelationshipsTrunkEntityRelationshipsRootEntityId" = r4."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" AND r1."Id1" = r4."RelationshipsBranchEntityId1" +ORDER BY r."Id" NULLS FIRST, r0."RelationshipsRootEntityId" NULLS FIRST, r1."RelationshipsTrunkEntityRelationshipsRootEntityId" NULLS FIRST, r1."Id1" NULLS FIRST, r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r2."RelationshipsBranchEntityId1" NULLS FIRST, r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r3."RelationshipsBranchEntityId1" NULLS FIRST, r4."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r4."RelationshipsBranchEntityId1" NULLS FIRST +"""); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedTableSplittingReferenceProjectionNoTrackingNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedTableSplittingReferenceProjectionNoTrackingNpgsqlTest.cs new file mode 100644 index 0000000000..f5f5702703 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Relationships/Projection/OwnedTableSplittingReferenceProjectionNoTrackingNpgsqlTest.cs @@ -0,0 +1,168 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit.Sdk; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Projection; + +public class OwnedTableSplittingReferenceProjectionNoTrackingNpgsqlTest + : OwnedTableSplittingReferenceProjectionNoTrackingRelationalTestBase +{ + public OwnedTableSplittingReferenceProjectionNoTrackingNpgsqlTest(OwnedTableSplittingRelationshipsNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Select_root(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_root(async)); + + Assert.Equal("42702", exception.SqlState); + } + + public override async Task Select_trunk_optional(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_trunk_optional(async)); + + Assert.Equal("42702", exception.SqlState); + } + + public override async Task Select_trunk_required(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_trunk_required(async)); + + Assert.Equal("42702", exception.SqlState); + } + + public override async Task Select_branch_required_required(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.Select_branch_required_required(async)); + + AssertSql( + """ +SELECT r1."RelationshipsTrunkEntityRelationshipsRootEntityId", r1."Name", r."Id", r0."RelationshipsRootEntityId", r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r4."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r4."Id1", r4."Name", r2."Name", r3."Name" +FROM "RootEntities" AS r +LEFT JOIN "Root_RequiredReferenceTrunk" AS r0 ON r."Id" = r0."RelationshipsRootEntityId" +LEFT JOIN "Root_RequiredReferenceTrunk_RequiredReferenceBranch" AS r1 ON r0."RelationshipsRootEntityId" = r1."RelationshipsTrunkEntityRelationshipsRootEntityId" +LEFT JOIN "Root_RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf" AS r2 ON r1."RelationshipsTrunkEntityRelationshipsRootEntityId" = r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +LEFT JOIN "Root_RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf" AS r3 ON r1."RelationshipsTrunkEntityRelationshipsRootEntityId" = r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +LEFT JOIN "Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf" AS r4 ON r1."RelationshipsTrunkEntityRelationshipsRootEntityId" = r4."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +ORDER BY r."Id" NULLS FIRST, r0."RelationshipsRootEntityId" NULLS FIRST, r1."RelationshipsTrunkEntityRelationshipsRootEntityId" NULLS FIRST, r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r4."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST +"""); + } + + public override async Task Select_branch_required_optional(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.Select_branch_required_optional(async)); + + AssertSql( + """ +SELECT r1."RelationshipsTrunkEntityRelationshipsRootEntityId", r1."Name", r."Id", r0."RelationshipsRootEntityId", r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r4."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r4."Id1", r4."Name", r2."Name", r3."Name" +FROM "RootEntities" AS r +LEFT JOIN "Root_RequiredReferenceTrunk" AS r0 ON r."Id" = r0."RelationshipsRootEntityId" +LEFT JOIN "Root_RequiredReferenceTrunk_OptionalReferenceBranch" AS r1 ON r0."RelationshipsRootEntityId" = r1."RelationshipsTrunkEntityRelationshipsRootEntityId" +LEFT JOIN "Root_RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf" AS r2 ON r1."RelationshipsTrunkEntityRelationshipsRootEntityId" = r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +LEFT JOIN "Root_RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf" AS r3 ON r1."RelationshipsTrunkEntityRelationshipsRootEntityId" = r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +LEFT JOIN "Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf" AS r4 ON r1."RelationshipsTrunkEntityRelationshipsRootEntityId" = r4."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +ORDER BY r."Id" NULLS FIRST, r0."RelationshipsRootEntityId" NULLS FIRST, r1."RelationshipsTrunkEntityRelationshipsRootEntityId" NULLS FIRST, r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r4."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST +"""); + } + + public override async Task Select_branch_optional_required(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.Select_branch_optional_required(async)); + + AssertSql( + """ +SELECT r1."RelationshipsTrunkEntityRelationshipsRootEntityId", r1."Name", r."Id", r0."RelationshipsRootEntityId", r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r4."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r4."Id1", r4."Name", r2."Name", r3."Name" +FROM "RootEntities" AS r +LEFT JOIN "Root_RequiredReferenceTrunk" AS r0 ON r."Id" = r0."RelationshipsRootEntityId" +LEFT JOIN "Root_RequiredReferenceTrunk_RequiredReferenceBranch" AS r1 ON r0."RelationshipsRootEntityId" = r1."RelationshipsTrunkEntityRelationshipsRootEntityId" +LEFT JOIN "Root_RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf" AS r2 ON r1."RelationshipsTrunkEntityRelationshipsRootEntityId" = r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +LEFT JOIN "Root_RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf" AS r3 ON r1."RelationshipsTrunkEntityRelationshipsRootEntityId" = r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +LEFT JOIN "Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf" AS r4 ON r1."RelationshipsTrunkEntityRelationshipsRootEntityId" = r4."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +ORDER BY r."Id" NULLS FIRST, r0."RelationshipsRootEntityId" NULLS FIRST, r1."RelationshipsTrunkEntityRelationshipsRootEntityId" NULLS FIRST, r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r4."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST +"""); + } + + public override async Task Select_branch_optional_optional(bool async) + { + // https://github.com/dotnet/efcore/pull/35942 + await Assert.ThrowsAsync(() => base.Select_branch_optional_optional(async)); + + AssertSql( + """ +SELECT r1."RelationshipsTrunkEntityRelationshipsRootEntityId", r1."Name", r."Id", r0."RelationshipsRootEntityId", r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r4."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~", r4."Id1", r4."Name", r2."Name", r3."Name" +FROM "RootEntities" AS r +LEFT JOIN "Root_RequiredReferenceTrunk" AS r0 ON r."Id" = r0."RelationshipsRootEntityId" +LEFT JOIN "Root_RequiredReferenceTrunk_OptionalReferenceBranch" AS r1 ON r0."RelationshipsRootEntityId" = r1."RelationshipsTrunkEntityRelationshipsRootEntityId" +LEFT JOIN "Root_RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf" AS r2 ON r1."RelationshipsTrunkEntityRelationshipsRootEntityId" = r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +LEFT JOIN "Root_RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf" AS r3 ON r1."RelationshipsTrunkEntityRelationshipsRootEntityId" = r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +LEFT JOIN "Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf" AS r4 ON r1."RelationshipsTrunkEntityRelationshipsRootEntityId" = r4."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" +ORDER BY r."Id" NULLS FIRST, r0."RelationshipsRootEntityId" NULLS FIRST, r1."RelationshipsTrunkEntityRelationshipsRootEntityId" NULLS FIRST, r2."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r3."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST, r4."RelationshipsBranchEntityRelationshipsTrunkEntityRelationships~" NULLS FIRST +"""); + } + + public override async Task Select_root_duplicated(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_root_duplicated(async)); + + Assert.Equal("42702", exception.SqlState); + } + + public override async Task Select_trunk_and_branch_duplicated(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_trunk_and_branch_duplicated(async)); + + Assert.Equal("42702", exception.SqlState); + } + + public override async Task Select_trunk_and_trunk_duplicated(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_trunk_and_trunk_duplicated(async)); + + Assert.Equal("42702", exception.SqlState); + } + + public override async Task Select_leaf_trunk_root(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_leaf_trunk_root(async)); + + Assert.Equal("42702", exception.SqlState); + } + + public override async Task Select_subquery_root_set_required_trunk_FirstOrDefault_branch(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_subquery_root_set_required_trunk_FirstOrDefault_branch(async)); + + Assert.Equal("42702", exception.SqlState); + } + + public override async Task Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(bool async) + { + // https://github.com/dotnet/efcore/issues/26993 + var exception = await Assert.ThrowsAsync(() => base.Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(async)); + + Assert.Equal("42702", exception.SqlState); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/TestUtilities/TestRelationalCommandBuilderFactory.cs b/test/EFCore.PG.FunctionalTests/TestUtilities/TestRelationalCommandBuilderFactory.cs index 8070dec02c..3a9732e376 100644 --- a/test/EFCore.PG.FunctionalTests/TestUtilities/TestRelationalCommandBuilderFactory.cs +++ b/test/EFCore.PG.FunctionalTests/TestUtilities/TestRelationalCommandBuilderFactory.cs @@ -42,16 +42,17 @@ public IRelationalCommand Build() => new TestRelationalCommand( Dependencies, Instance.ToString(), + Instance.ToString(), Parameters); - public IRelationalCommandBuilder Append(string value) + public IRelationalCommandBuilder Append(string value, bool redact = false) { Instance.Append(value); return this; } - public IRelationalCommandBuilder Append(FormattableString value) + public IRelationalCommandBuilder Append(FormattableString value, bool redact = false) { Instance.Append(value); @@ -86,14 +87,18 @@ public int CommandTextLength private class TestRelationalCommand( RelationalCommandBuilderDependencies dependencies, string commandText, + string logCommandText, IReadOnlyList parameters) : IRelationalCommand { - private readonly RelationalCommand _realRelationalCommand = new(dependencies, commandText, parameters); + private readonly RelationalCommand _realRelationalCommand = new(dependencies, commandText, logCommandText, parameters); public string CommandText => _realRelationalCommand.CommandText; + public string LogCommandText + => _realRelationalCommand.LogCommandText; + public IReadOnlyList Parameters => _realRelationalCommand.Parameters; diff --git a/test/EFCore.PG.Tests/NpgsqlRelationalConnectionTest.cs b/test/EFCore.PG.Tests/NpgsqlRelationalConnectionTest.cs index 3f7d9d3ed5..7e23c0a650 100644 --- a/test/EFCore.PG.Tests/NpgsqlRelationalConnectionTest.cs +++ b/test/EFCore.PG.Tests/NpgsqlRelationalConnectionTest.cs @@ -406,7 +406,8 @@ public static NpgsqlRelationalConnection CreateConnection(DbContextOptions? opti TestServiceFactory.Instance.Create(), new NpgsqlSqlGenerationHelper(new RelationalSqlGenerationHelperDependencies()), new NpgsqlSingletonOptions()), - new ExceptionDetector()))), + new ExceptionDetector(), + new LoggingOptions()))), new NpgsqlDataSourceManager([]), dbContextOptions); } diff --git a/test/EFCore.PG.Tests/Storage/NpgsqlTypeMappingTest.cs b/test/EFCore.PG.Tests/Storage/NpgsqlTypeMappingTest.cs index ea8f3663af..d7a16e8701 100644 --- a/test/EFCore.PG.Tests/Storage/NpgsqlTypeMappingTest.cs +++ b/test/EFCore.PG.Tests/Storage/NpgsqlTypeMappingTest.cs @@ -915,12 +915,10 @@ public void GenerateCodeLiteral_returns_json_document_literal() [Fact] public void GenerateCodeLiteral_returns_json_element_literal() - // TODO: https://github.com/dotnet/efcore/issues/32192 - => Assert.Throws(() => CodeLiteral(JsonDocument.Parse("""{"Name":"Joe","Age":25}""").RootElement)); + => Assert.Equal( + """System.Text.Json.JsonDocument.Parse("{\"Name\":\"Joe\",\"Age\":25}", new System.Text.Json.JsonDocumentOptions()).RootElement""", + CodeLiteral(JsonDocument.Parse(@"{""Name"":""Joe"",""Age"":25}").RootElement)); - // Assert.Equal( - // """System.Text.Json.JsonDocument.Parse("{\"Name\":\"Joe\",\"Age\":25}", new System.Text.Json.JsonDocumentOptions()).RootElement""", - // CodeLiteral(JsonDocument.Parse(@"{""Name"":""Joe"",""Age"":25}").RootElement)); [Fact] public void ValueComparer_JsonDocument() { diff --git a/test/EFCore.PG.Tests/TestUtilities/FakeRelationalCommandDiagnosticsLogger.cs b/test/EFCore.PG.Tests/TestUtilities/FakeRelationalCommandDiagnosticsLogger.cs index a7573b37e8..c4d96a3167 100644 --- a/test/EFCore.PG.Tests/TestUtilities/FakeRelationalCommandDiagnosticsLogger.cs +++ b/test/EFCore.PG.Tests/TestUtilities/FakeRelationalCommandDiagnosticsLogger.cs @@ -1,8 +1,9 @@ -using System.Data.Common; +#nullable enable -namespace Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities; +using System.Data.Common; +using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities; -#nullable enable +namespace Microsoft.EntityFrameworkCore.TestUtilities; public class FakeRelationalCommandDiagnosticsLogger : FakeDiagnosticsLogger, IRelationalCommandDiagnosticsLogger @@ -44,6 +45,7 @@ public DbCommand CommandInitialized( public InterceptionResult CommandReaderExecuting( IRelationalConnection connection, DbCommand command, + string logCommandText, DbContext? context, Guid commandId, Guid connectionId, @@ -54,6 +56,7 @@ public InterceptionResult CommandReaderExecuting( public InterceptionResult CommandScalarExecuting( IRelationalConnection connection, DbCommand command, + string logCommandText, DbContext? context, Guid commandId, Guid connectionId, @@ -64,6 +67,7 @@ public InterceptionResult CommandScalarExecuting( public InterceptionResult CommandNonQueryExecuting( IRelationalConnection connection, DbCommand command, + string logCommandText, DbContext? context, Guid commandId, Guid connectionId, @@ -74,6 +78,7 @@ public InterceptionResult CommandNonQueryExecuting( public ValueTask> CommandReaderExecutingAsync( IRelationalConnection connection, DbCommand command, + string logCommandText, DbContext? context, Guid commandId, Guid connectionId, @@ -85,6 +90,7 @@ public ValueTask> CommandReaderExecutingAsync( public ValueTask> CommandScalarExecutingAsync( IRelationalConnection connection, DbCommand command, + string logCommandText, DbContext? context, Guid commandId, Guid connectionId, @@ -96,6 +102,7 @@ public ValueTask> CommandScalarExecutingAsync( public ValueTask> CommandNonQueryExecutingAsync( IRelationalConnection connection, DbCommand command, + string logCommandText, DbContext? context, Guid commandId, Guid connectionId, @@ -107,6 +114,7 @@ public ValueTask> CommandNonQueryExecutingAsync( public DbDataReader CommandReaderExecuted( IRelationalConnection connection, DbCommand command, + string logCommandText, DbContext? context, Guid commandId, Guid connectionId, @@ -119,6 +127,7 @@ public DbDataReader CommandReaderExecuted( public object? CommandScalarExecuted( IRelationalConnection connection, DbCommand command, + string logCommandText, DbContext? context, Guid commandId, Guid connectionId, @@ -131,6 +140,7 @@ public DbDataReader CommandReaderExecuted( public int CommandNonQueryExecuted( IRelationalConnection connection, DbCommand command, + string logCommandText, DbContext? context, Guid commandId, Guid connectionId, @@ -143,6 +153,7 @@ public int CommandNonQueryExecuted( public ValueTask CommandReaderExecutedAsync( IRelationalConnection connection, DbCommand command, + string logCommandText, DbContext? context, Guid commandId, Guid connectionId, @@ -156,6 +167,7 @@ public ValueTask CommandReaderExecutedAsync( public ValueTask CommandScalarExecutedAsync( IRelationalConnection connection, DbCommand command, + string logCommandText, DbContext? context, Guid commandId, Guid connectionId, @@ -169,6 +181,7 @@ public ValueTask CommandReaderExecutedAsync( public ValueTask CommandNonQueryExecutedAsync( IRelationalConnection connection, DbCommand command, + string logCommandText, DbContext? context, Guid commandId, Guid connectionId, @@ -179,9 +192,38 @@ public ValueTask CommandNonQueryExecutedAsync( CancellationToken cancellationToken = default) => new(methodResult); + public void CommandException( + IRelationalConnection connection, + DbCommand command, + DbContext? context, + DbCommandMethod executeMethod, + Guid commandId, + Guid connectionId, + Exception exception, + DateTimeOffset startTime, + TimeSpan duration, + CommandSource commandSource) + { + } + + public Task CommandExceptionAsync( + IRelationalConnection connection, + DbCommand command, + DbContext? context, + DbCommandMethod executeMethod, + Guid commandId, + Guid connectionId, + Exception exception, + DateTimeOffset startTime, + TimeSpan duration, + CommandSource commandSource, + CancellationToken cancellationToken = default) + => Task.CompletedTask; + public void CommandError( IRelationalConnection connection, DbCommand command, + string logCommandText, DbContext? context, DbCommandMethod executeMethod, Guid commandId, @@ -196,6 +238,7 @@ public void CommandError( public Task CommandErrorAsync( IRelationalConnection connection, DbCommand command, + string logCommandText, DbContext? context, DbCommandMethod executeMethod, Guid commandId, @@ -210,6 +253,7 @@ public Task CommandErrorAsync( public void CommandCanceled( IRelationalConnection connection, DbCommand command, + string logCommandText, DbContext? context, DbCommandMethod executeMethod, Guid commandId, @@ -223,6 +267,7 @@ public void CommandCanceled( public Task CommandCanceledAsync( IRelationalConnection connection, DbCommand command, + string logCommandText, DbContext? context, DbCommandMethod executeMethod, Guid commandId, diff --git a/test/EFCore.PG.Tests/Update/NpgsqlModificationCommandBatchFactoryTest.cs b/test/EFCore.PG.Tests/Update/NpgsqlModificationCommandBatchFactoryTest.cs index 86fadf80fa..0f4ad43b3e 100644 --- a/test/EFCore.PG.Tests/Update/NpgsqlModificationCommandBatchFactoryTest.cs +++ b/test/EFCore.PG.Tests/Update/NpgsqlModificationCommandBatchFactoryTest.cs @@ -1,3 +1,4 @@ +using Microsoft.EntityFrameworkCore.Diagnostics.Internal; using Microsoft.EntityFrameworkCore.Infrastructure.Internal; using Microsoft.EntityFrameworkCore.Storage.Internal; using Microsoft.EntityFrameworkCore.Update.Internal; @@ -27,7 +28,8 @@ public void Uses_MaxBatchSize_specified_in_NpgsqlOptionsExtension() new RelationalCommandBuilderFactory( new RelationalCommandBuilderDependencies( typeMapper, - new ExceptionDetector())), + new ExceptionDetector(), + new LoggingOptions())), new NpgsqlSqlGenerationHelper( new RelationalSqlGenerationHelperDependencies()), new NpgsqlUpdateSqlGenerator( @@ -63,7 +65,8 @@ public void MaxBatchSize_is_optional() new RelationalCommandBuilderFactory( new RelationalCommandBuilderDependencies( typeMapper, - new ExceptionDetector())), + new ExceptionDetector(), + new LoggingOptions())), new NpgsqlSqlGenerationHelper( new RelationalSqlGenerationHelperDependencies()), new NpgsqlUpdateSqlGenerator( diff --git a/test/EFCore.PG.Tests/Update/NpgsqlModificationCommandBatchTest.cs b/test/EFCore.PG.Tests/Update/NpgsqlModificationCommandBatchTest.cs index c242266e0d..d9a254a620 100644 --- a/test/EFCore.PG.Tests/Update/NpgsqlModificationCommandBatchTest.cs +++ b/test/EFCore.PG.Tests/Update/NpgsqlModificationCommandBatchTest.cs @@ -1,3 +1,4 @@ +using Microsoft.EntityFrameworkCore.Diagnostics.Internal; using Microsoft.EntityFrameworkCore.Infrastructure.Internal; using Microsoft.EntityFrameworkCore.Storage.Internal; using Microsoft.EntityFrameworkCore.Update.Internal; @@ -25,7 +26,8 @@ public void AddCommand_returns_false_when_max_batch_size_is_reached() new RelationalCommandBuilderFactory( new RelationalCommandBuilderDependencies( typeMapper, - new ExceptionDetector())), + new ExceptionDetector(), + new LoggingOptions())), new NpgsqlSqlGenerationHelper( new RelationalSqlGenerationHelperDependencies()), new NpgsqlUpdateSqlGenerator(