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