diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8144c1aeef..2c91915d60 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -56,6 +56,8 @@ jobs: - name: Setup .NET Core SDK uses: actions/setup-dotnet@v5 + with: + global-json-file: global.json - name: Build run: dotnet build --configuration Debug @@ -96,7 +98,7 @@ jobs: # Install PostgreSQL echo "Installing PostgreSQL (version: ${EDB_VERSION})" - curl -o pgsql.zip -L https://get.enterprisedb.com/postgresql/postgresql-${EDB_VERSION}-windows-x64-binaries.zip + curl --fail --retry 5 --retry-all-errors -o pgsql.zip -L https://get.enterprisedb.com/postgresql/postgresql-${EDB_VERSION}-windows-x64-binaries.zip unzip pgsql.zip -x 'pgsql/include/**' 'pgsql/doc/**' 'pgsql/pgAdmin 4/**' 'pgsql/StackBuilder/**' # Match Npgsql CI Docker image and stash one level up @@ -112,7 +114,7 @@ jobs: shell: bash - name: Test - run: dotnet test -c ${{ matrix.config }} --logger "GitHubActions;report-warnings=false" + run: dotnet test -c ${{ matrix.config }} --filter-not-trait category=failing shell: bash env: # Disable SSL to avoid unnecessary handshake pressure in the Windows functional tests. @@ -143,6 +145,8 @@ jobs: - name: Setup .NET Core SDK uses: actions/setup-dotnet@v5 + with: + global-json-file: global.json - name: Pack run: dotnet pack --configuration Release --property:PackageOutputPath="$PWD/nupkgs" --version-suffix "ci.$(date -u +%Y%m%dT%H%M%S)+sha.${GITHUB_SHA:0:9}" -p:ContinuousIntegrationBuild=true @@ -177,6 +181,8 @@ jobs: - name: Setup .NET Core SDK uses: actions/setup-dotnet@v5 + with: + global-json-file: global.json - name: Pack run: dotnet pack --configuration Release --property:PackageOutputPath="$PWD/nupkgs" -p:ContinuousIntegrationBuild=true diff --git a/Directory.Build.props b/Directory.Build.props index 789e2c3a55..2daa2e5114 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 11.0.0-preview.5 + 11.0.0-preview.6 net11.0 latest enable diff --git a/Directory.Packages.props b/Directory.Packages.props index 5712d55ee7..eeb1d7ef9d 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,8 +1,8 @@ - 11.0.0-preview.5.26311.105 - 11.0.0-preview.5.26311.105 - 11.0.0-preview.5.26311.105 + 11.0.0-preview.6.26311.113 + 11.0.0-preview.6.26311.113 + 11.0.0-preview.6.26311.113 10.0.3 @@ -28,11 +28,9 @@ - - - + + - diff --git a/NuGet.config b/NuGet.config index 98c370333c..df92127802 100644 --- a/NuGet.config +++ b/NuGet.config @@ -21,6 +21,9 @@ + + + diff --git a/empty b/empty new file mode 100644 index 0000000000..e69de29bb2 diff --git a/global.json b/global.json index 973ee8c8d8..c31ea3c747 100644 --- a/global.json +++ b/global.json @@ -1,7 +1,9 @@ { "sdk": { - "version": "11.0.100-preview.4.26210.111", - "rollForward": "latestMinor", + "version": "11.0.100-preview.6.26313.102", "allowPrerelease": true + }, + "test": { + "runner": "Microsoft.Testing.Platform" } } diff --git a/src/EFCore.PG/Update/Internal/NpgsqlUpdateSqlGenerator.cs b/src/EFCore.PG/Update/Internal/NpgsqlUpdateSqlGenerator.cs index 92b92edc64..dbe3327ae8 100644 --- a/src/EFCore.PG/Update/Internal/NpgsqlUpdateSqlGenerator.cs +++ b/src/EFCore.PG/Update/Internal/NpgsqlUpdateSqlGenerator.cs @@ -147,6 +147,7 @@ protected override void AppendUpdateColumnValue( Check.DebugAssert(columnModification.TypeMapping.StoreType is "jsonb", "Non-jsonb type mapping in JSON partial update"); var jsonPath = columnModification.JsonPath; + var indices = jsonPath.Indices; // TODO: Lax or not? stringBuilder @@ -166,7 +167,8 @@ protected override void AppendUpdateColumnValue( if (segment.IsArray) { - stringBuilder.Append(jsonPath.Indices[ordinalIndex++]); + Check.DebugAssert(indices is not null, "Array segment found in JSON path with no indices"); + stringBuilder.Append(indices[ordinalIndex++]); } else { diff --git a/test/Directory.Build.props b/test/Directory.Build.props index b4dbbaa8a6..f9867dc04f 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -5,16 +5,17 @@ false false + true - $(NoWarn);xUnit1003;xUnit1004;xUnit1013;xUnit1024;EF1001 + $(NoWarn);CS0618;xUnit1003;xUnit1004;xUnit1008;xUnit1013;xUnit1024;xUnit1051;EF1001 - + + - @@ -24,7 +25,6 @@ - diff --git a/test/EFCore.PG.FunctionalTests/BuiltInDataTypesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BuiltInDataTypesNpgsqlTest.cs index 5f9ba85d85..a1bbe23c05 100644 --- a/test/EFCore.PG.FunctionalTests/BuiltInDataTypesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BuiltInDataTypesNpgsqlTest.cs @@ -719,7 +719,6 @@ private static void AssertNullMappedNullableDataTypes(MappedNullableDataTypes en Assert.Null(entity.Mood); } - [ConditionalFact(Skip = "DateTimeOffset with non-zero offset, https://github.com/dotnet/efcore/issues/26068")] public override Task Can_insert_and_read_back_object_backed_data_types() => Task.CompletedTask; diff --git a/test/EFCore.PG.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesNpgsqlTest.cs index ba1fce5126..13556480aa 100644 --- a/test/EFCore.PG.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesNpgsqlTest.cs @@ -613,15 +613,19 @@ public override async Task Delete_with_RightJoin(bool async) DELETE FROM "Order Details" AS o WHERE EXISTS ( SELECT 1 - FROM "Order Details" AS o0 + FROM ( + SELECT o1."OrderID", o1."ProductID" + FROM "Order Details" AS o1 + WHERE o1."OrderID" < 10276 + ) AS o0 RIGHT JOIN ( - SELECT o2."OrderID" - FROM "Orders" AS o2 - WHERE o2."OrderID" < 10300 - ORDER BY o2."OrderID" NULLS FIRST + SELECT o3."OrderID" + FROM "Orders" AS o3 + WHERE o3."OrderID" < 10300 + ORDER BY o3."OrderID" NULLS FIRST LIMIT @p1 OFFSET @p - ) AS o1 ON o0."OrderID" = o1."OrderID" - WHERE o0."OrderID" < 10276 AND o0."OrderID" = o."OrderID" AND o0."ProductID" = o."ProductID") + ) AS o2 ON o0."OrderID" = o2."OrderID" + WHERE o0."OrderID" = o."OrderID" AND o0."ProductID" = o."ProductID") """); } @@ -1318,25 +1322,28 @@ await AssertUpdate( e => e.Order, s => s.SetProperty(t => t.Order.OrderDate, new DateTime(2020, 1, 1, 0, 0, 0)), rowsAffectedCount: 2, - (b, a) => Assert.All(a, o => Assert.Equal(new DateTime(2020, 1, 1, 0, 0, 0), o.OrderDate))); + (b, a) => Assert.All(a.Where(o => o is not null), o => Assert.Equal(new DateTime(2020, 1, 1, 0, 0, 0), o.OrderDate))); AssertExecuteUpdateSql( """ @p='2020-01-01T00:00:00.0000000' (Nullable = true) -UPDATE "Orders" AS o0 +UPDATE "Orders" AS o1 SET "OrderDate" = @p FROM ( - SELECT o."OrderID" - FROM "Orders" AS o + SELECT o0."OrderID" + FROM ( + SELECT o."OrderID", o."CustomerID" + FROM "Orders" AS o + WHERE o."OrderID" < 10300 + ) AS o0 RIGHT JOIN ( SELECT c."CustomerID" FROM "Customers" AS c WHERE c."CustomerID" LIKE 'F%' - ) AS c0 ON o."CustomerID" = c0."CustomerID" - WHERE o."OrderID" < 10300 + ) AS c0 ON o0."CustomerID" = c0."CustomerID" ) AS s -WHERE o0."OrderID" = s."OrderID" +WHERE o1."OrderID" = s."OrderID" """); } @@ -1407,7 +1414,6 @@ WHERE c."CustomerID" LIKE 'F%' """); } - [ConditionalTheory] public override async Task Update_with_cross_join_left_join_set_constant(bool async) { await base.Update_with_cross_join_left_join_set_constant(async); @@ -1437,7 +1443,6 @@ WHERE c."CustomerID" LIKE 'F%' """); } - [ConditionalTheory] public override async Task Update_with_cross_join_cross_apply_set_constant(bool async) { await base.Update_with_cross_join_cross_apply_set_constant(async); @@ -1467,7 +1472,6 @@ WHERE c."CustomerID" LIKE 'F%' """); } - [ConditionalTheory] public override async Task Update_with_cross_join_outer_apply_set_constant(bool async) { await base.Update_with_cross_join_outer_apply_set_constant(async); @@ -1577,8 +1581,6 @@ WHERE c."CustomerID" LIKE 'F%' """); } - [ConditionalTheory] - [MemberData(nameof(IsAsyncData))] public override async Task Update_with_two_inner_joins(bool async) { await AssertUpdate( diff --git a/test/EFCore.PG.FunctionalTests/ComputedColumnTest.cs b/test/EFCore.PG.FunctionalTests/ComputedColumnTest.cs index 46de11410b..33ca759145 100644 --- a/test/EFCore.PG.FunctionalTests/ComputedColumnTest.cs +++ b/test/EFCore.PG.FunctionalTests/ComputedColumnTest.cs @@ -134,9 +134,9 @@ public void Can_use_computed_columns_with_nullable_enum() protected NpgsqlTestStore TestStore { get; private set; } = null!; - public async Task InitializeAsync() + public async ValueTask InitializeAsync() => TestStore = await NpgsqlTestStore.CreateInitializedAsync("ComputedColumnTest"); - public async Task DisposeAsync() + public async ValueTask DisposeAsync() => await TestStore.DisposeAsync(); } diff --git a/test/EFCore.PG.FunctionalTests/ConnectionInterceptionNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/ConnectionInterceptionNpgsqlTest.cs index ad7b852f13..4dd462b460 100644 --- a/test/EFCore.PG.FunctionalTests/ConnectionInterceptionNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/ConnectionInterceptionNpgsqlTest.cs @@ -7,21 +7,176 @@ namespace Microsoft.EntityFrameworkCore; public abstract class ConnectionInterceptionNpgsqlTestBase(ConnectionInterceptionNpgsqlTestBase.InterceptionNpgsqlFixtureBase fixture) : ConnectionInterceptionTestBase(fixture) { - [ConditionalTheory(Skip = "#2368")] - public override Task Intercept_connection_creation_passively(bool async) - => base.Intercept_connection_creation_passively(async); + public override async Task Intercept_connection_creation_passively(bool async) + { + var interceptor = new ConnectionCreationInterceptor(); + var context = new ConnectionStringContext(ConfigureProvider); + context.Interceptors.Add(interceptor); + + _ = context.Model; + + Assert.False(interceptor.CreatingCalled); + Assert.False(interceptor.CreatedCalled); + Assert.False(interceptor.DisposingCalled); + Assert.False(interceptor.DisposedCalled); + + var connection = context.Database.GetDbConnection(); + + Assert.True(interceptor.CreatingCalled); + Assert.True(interceptor.CreatedCalled); + Assert.False(interceptor.DisposingCalled); + Assert.False(interceptor.DisposedCalled); + Assert.Same(context, interceptor.Context); + Assert.Same(connection, interceptor.Connection); + Assert.Equal(connection.ConnectionString, interceptor.ConnectionString ?? ""); + + if (async) + { + await context.DisposeAsync(); + } + else + { + context.Dispose(); + } + + Assert.True(interceptor.DisposingCalled); + Assert.True(interceptor.DisposedCalled); + Assert.Equal(async, interceptor.AsyncCalled); + Assert.NotEqual(async, interceptor.SyncCalled); + } + + public override async Task Intercept_connection_creation_with_multiple_interceptors(bool async) + { + using var tempContext1 = new ConnectionStringContext(ConfigureProvider); + var replacementConnection1 = tempContext1.Database.GetDbConnection(); + + using var tempContext2 = new ConnectionStringContext(ConfigureProvider); + var replacementConnection2 = tempContext2.Database.GetDbConnection(); + + var interceptors = new[] + { + new ConnectionCreationInterceptor(), + new ConnectionCreationOverrideInterceptor(replacementConnection1), + new ConnectionCreationInterceptor(), + new ConnectionCreationOverrideInterceptor(replacementConnection2) + }; + + var context = new ConnectionStringContext(ConfigureProvider); + context.Interceptors.AddRange(interceptors); + + var connection = context.Database.GetDbConnection(); + Assert.Same(replacementConnection2, connection); + + foreach (var interceptor in interceptors) + { + Assert.True(interceptor.CreatingCalled); + Assert.True(interceptor.CreatedCalled); + Assert.False(interceptor.DisposingCalled); + Assert.False(interceptor.DisposedCalled); + Assert.Same(context, interceptor.Context); + } + + if (async) + { + await context.DisposeAsync(); + } + else + { + context.Dispose(); + } + + foreach (var interceptor in interceptors) + { + Assert.True(interceptor.DisposingCalled); + Assert.True(interceptor.DisposedCalled); + Assert.Equal(async, interceptor.AsyncCalled); + Assert.NotEqual(async, interceptor.SyncCalled); + } + } + + public override async Task Intercept_connection_to_override_connection_after_creation(bool async) + { + using var tempContext = new ConnectionStringContext(ConfigureProvider); + var replacementConnection = tempContext.Database.GetDbConnection(); + + var interceptor = new ConnectionCreationReplaceInterceptor(replacementConnection); + var context = new ConnectionStringContext(ConfigureProvider); + context.Interceptors.Add(interceptor); + + _ = context.Model; + + Assert.False(interceptor.CreatingCalled); + Assert.False(interceptor.CreatedCalled); + Assert.False(interceptor.DisposingCalled); + Assert.False(interceptor.DisposedCalled); + + var connection = context.Database.GetDbConnection(); + Assert.Same(replacementConnection, connection); + + Assert.True(interceptor.CreatingCalled); + Assert.True(interceptor.CreatedCalled); + Assert.False(interceptor.DisposingCalled); + Assert.False(interceptor.DisposedCalled); + Assert.Same(context, interceptor.Context); + Assert.Same(connection, interceptor.Connection); + Assert.Equal(connection.ConnectionString, interceptor.ConnectionString ?? ""); + + if (async) + { + await context.DisposeAsync(); + } + else + { + context.Dispose(); + } + + Assert.True(interceptor.DisposingCalled); + Assert.True(interceptor.DisposedCalled); + Assert.Equal(async, interceptor.AsyncCalled); + Assert.NotEqual(async, interceptor.SyncCalled); + } + + public override async Task Intercept_connection_to_override_creation(bool async) + { + using var tempContext = new ConnectionStringContext(ConfigureProvider); + var replacementConnection = tempContext.Database.GetDbConnection(); + + var interceptor = new ConnectionCreationOverrideInterceptor(replacementConnection); + var context = new ConnectionStringContext(ConfigureProvider); + context.Interceptors.Add(interceptor); + + _ = context.Model; - [ConditionalTheory(Skip = "#2368")] - public override Task Intercept_connection_creation_with_multiple_interceptors(bool async) - => base.Intercept_connection_creation_with_multiple_interceptors(async); + Assert.False(interceptor.CreatingCalled); + Assert.False(interceptor.CreatedCalled); + Assert.False(interceptor.DisposingCalled); + Assert.False(interceptor.DisposedCalled); - [ConditionalTheory(Skip = "#2368")] - public override Task Intercept_connection_to_override_connection_after_creation(bool async) - => base.Intercept_connection_to_override_connection_after_creation(async); + var connection = context.Database.GetDbConnection(); + Assert.Same(replacementConnection, connection); - [ConditionalTheory(Skip = "#2368")] - public override Task Intercept_connection_to_override_creation(bool async) - => base.Intercept_connection_to_override_creation(async); + Assert.True(interceptor.CreatingCalled); + Assert.True(interceptor.CreatedCalled); + Assert.False(interceptor.DisposingCalled); + Assert.False(interceptor.DisposedCalled); + Assert.Same(context, interceptor.Context); + Assert.Same(connection, interceptor.Connection); + Assert.Equal(connection.ConnectionString, interceptor.ConnectionString ?? ""); + + if (async) + { + await context.DisposeAsync(); + } + else + { + context.Dispose(); + } + + Assert.True(interceptor.DisposingCalled); + Assert.True(interceptor.DisposedCalled); + Assert.Equal(async, interceptor.AsyncCalled); + Assert.NotEqual(async, interceptor.SyncCalled); + } public abstract class InterceptionNpgsqlFixtureBase : InterceptionFixtureBase { @@ -38,7 +193,7 @@ protected override IServiceCollection InjectInterceptors( } protected override DbContextOptionsBuilder ConfigureProvider(DbContextOptionsBuilder optionsBuilder) - => optionsBuilder.UseNpgsql(); + => optionsBuilder.UseNpgsql("Host=localhost;Database=Dummy"); protected override BadUniverseContext CreateBadUniverse(DbContextOptionsBuilder optionsBuilder) => new(optionsBuilder.UseNpgsql(new FakeDbConnection()).Options); diff --git a/test/EFCore.PG.FunctionalTests/ConvertToProviderTypesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/ConvertToProviderTypesNpgsqlTest.cs index ee2ddc0025..cd0c98806d 100644 --- a/test/EFCore.PG.FunctionalTests/ConvertToProviderTypesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/ConvertToProviderTypesNpgsqlTest.cs @@ -49,7 +49,6 @@ public ConvertToProviderTypesNpgsqlTest(ConvertToProviderTypesNpgsqlFixture fixt // } // } - [ConditionalFact(Skip = "DateTimeOffset with non-zero offset, https://github.com/dotnet/efcore/issues/26068")] public override Task Can_insert_and_read_back_object_backed_data_types() => Task.CompletedTask; diff --git a/test/EFCore.PG.FunctionalTests/CustomConvertersNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/CustomConvertersNpgsqlTest.cs index faeb6c72b7..2273b61fe8 100644 --- a/test/EFCore.PG.FunctionalTests/CustomConvertersNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/CustomConvertersNpgsqlTest.cs @@ -7,7 +7,6 @@ public class CustomConvertersNpgsqlTest(CustomConvertersNpgsqlTest.CustomConvert public override Task Can_insert_and_read_back_with_case_insensitive_string_key() => Task.CompletedTask; - [ConditionalFact(Skip = "DateTimeOffset with non-zero offset, https://github.com/dotnet/efcore/issues/26068")] public override Task Can_insert_and_read_back_object_backed_data_types() => Task.CompletedTask; diff --git a/test/EFCore.PG.FunctionalTests/JsonTypesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/JsonTypesNpgsqlTest.cs index 7446bed8cd..d47db88de4 100644 --- a/test/EFCore.PG.FunctionalTests/JsonTypesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/JsonTypesNpgsqlTest.cs @@ -210,7 +210,6 @@ protected class NpgsqlDateOnlyCollectionType public List DateOnly { get; set; } = null!; } - [ConditionalFact] public override Task Can_read_write_collection_of_nullable_DateOnly_JSON_values() => Can_read_and_write_JSON_value>( nameof(NullableDateOnlyCollectionType.DateOnly), @@ -462,7 +461,6 @@ public virtual Task Can_read_write_LogSequenceNumber_JSON_values(ulong value, st new NpgsqlLogSequenceNumber(value), json); - [ConditionalFact] public override async Task Can_read_write_point() { var factory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326); @@ -480,7 +478,6 @@ public virtual async Task Can_read_write_point_without_SRID() new Point(2, 4), """{"Prop":"POINT (2 4)"}"""); - [ConditionalFact] public override async Task Can_read_write_point_with_M() { var factory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326); @@ -511,7 +508,6 @@ await Can_read_and_write_JSON_value( """{"Prop":"SRID=4326;POINT Z(1 2 3)"}"""); } - [ConditionalFact] public override async Task Can_read_write_line_string() { var factory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326); diff --git a/test/EFCore.PG.FunctionalTests/LazyLoadProxyNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/LazyLoadProxyNpgsqlTest.cs index d8c871ae5b..db7bb1684d 100644 --- a/test/EFCore.PG.FunctionalTests/LazyLoadProxyNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/LazyLoadProxyNpgsqlTest.cs @@ -15,10 +15,8 @@ public LazyLoadProxyNpgsqlTest(LoadNpgsqlFixture fixture) public override void Can_serialize_proxies_to_JSON() => Assert.Throws(base.Can_serialize_proxies_to_JSON); - [ConditionalFact] // Requires MARS public override void Top_level_projection_track_entities_before_passing_to_client_method() { } - [ConditionalTheory(Skip = "Possibly requires MARS, investigate")] public override void Lazy_load_one_to_one_reference_with_recursive_property(EntityState state) => base.Lazy_load_one_to_one_reference_with_recursive_property(state); diff --git a/test/EFCore.PG.FunctionalTests/Migrations/MigrationsInfrastructureNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Migrations/MigrationsInfrastructureNpgsqlTest.cs index d7a66ed60a..d8181014b4 100644 --- a/test/EFCore.PG.FunctionalTests/Migrations/MigrationsInfrastructureNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Migrations/MigrationsInfrastructureNpgsqlTest.cs @@ -29,23 +29,18 @@ public override Task Can_apply_two_migrations_in_transaction_async() public override Task Can_generate_no_migration_script() => base.Can_generate_no_migration_script(); - [ConditionalFact(Skip = "https://github.com/dotnet/efcore/issues/33056")] public override void Can_apply_all_migrations() => base.Can_apply_all_migrations(); - [ConditionalFact(Skip = "https://github.com/dotnet/efcore/issues/33056")] public override void Can_apply_range_of_migrations() => base.Can_apply_range_of_migrations(); - [ConditionalFact(Skip = "https://github.com/dotnet/efcore/issues/33056")] public override void Can_revert_all_migrations() => base.Can_revert_all_migrations(); - [ConditionalFact(Skip = "https://github.com/dotnet/efcore/issues/33056")] public override void Can_revert_one_migrations() => base.Can_revert_one_migrations(); - [ConditionalFact(Skip = "https://github.com/dotnet/efcore/issues/33056")] public override Task Can_apply_all_migrations_async() => base.Can_apply_all_migrations_async(); diff --git a/test/EFCore.PG.FunctionalTests/Migrations/MigrationsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Migrations/MigrationsNpgsqlTest.cs index 0d69461cfa..bfdc208ae5 100644 --- a/test/EFCore.PG.FunctionalTests/Migrations/MigrationsNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Migrations/MigrationsNpgsqlTest.cs @@ -3246,7 +3246,6 @@ public override async Task Add_required_primitive_collection_with_custom_default AssertSql("""ALTER TABLE "Customers" ADD "Numbers" integer[] NOT NULL DEFAULT (ARRAY[1,2,3]);"""); } - [ConditionalFact(Skip = "issue #33038")] public override async Task Add_required_primitive_collection_with_custom_converter_to_existing_table() { await base.Add_required_primitive_collection_with_custom_converter_to_existing_table(); @@ -3337,7 +3336,6 @@ public override async Task Add_required_primitve_collection_with_custom_default_ AssertSql("""ALTER TABLE "Customers" ADD "Numbers" integer[] NOT NULL DEFAULT ARRAY[1,2,3]::integer[];"""); } - [ConditionalFact(Skip = "issue #33038")] public override async Task Add_required_primitve_collection_with_custom_converter_to_existing_table() { await base.Add_required_primitve_collection_with_custom_converter_to_existing_table(); diff --git a/test/EFCore.PG.FunctionalTests/Query/AdHocMiscellaneousQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/AdHocMiscellaneousQueryNpgsqlTest.cs index 9067340a93..1c4546f471 100644 --- a/test/EFCore.PG.FunctionalTests/Query/AdHocMiscellaneousQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/AdHocMiscellaneousQueryNpgsqlTest.cs @@ -33,7 +33,51 @@ public override Task SelectMany_where_Select(bool async) public override Task Subquery_first_member_compared_to_null(bool async) => Task.CompletedTask; - [ConditionalTheory(Skip = "https://github.com/dotnet/efcore/pull/27995/files#r874038747")] - public override Task StoreType_for_UDF_used(bool async) - => base.StoreType_for_UDF_used(async); + public override async Task StoreType_for_UDF_used(bool async) + { + var contextFactory = await InitializeNonSharedTest(); + using var context = contextFactory.CreateDbContext(); + + var date = new DateTime(2012, 12, 12); + var query1 = context.Set().Where(x => x.SomeDate == date); + var query2 = context.Set().Where(x => Context27954Npgsql.MyEntity.Modify(x.SomeDate) == date); + + if (async) + { + await query1.ToListAsync(); + await Assert.ThrowsAnyAsync(() => query2.ToListAsync()); + } + else + { + query1.ToList(); + Assert.ThrowsAny(() => query2.ToList()); + } + } + + protected class Context27954Npgsql(DbContextOptions options) : DbContext(options) + { + public DbSet MyEntities { get; set; } = null!; + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder + .HasDbFunction(typeof(MyEntity).GetMethod(nameof(MyEntity.Modify))!) + .HasName("ModifyDate") + .HasStoreType("timestamp without time zone"); + + modelBuilder.Entity() + .Property(e => e.SomeDate) + .HasColumnType("timestamp without time zone"); + } + + public class MyEntity + { + public int Id { get; set; } + + public DateTime SomeDate { get; set; } + + public static DateTime Modify(DateTime date) + => throw new NotSupportedException(); + } + } } diff --git a/test/EFCore.PG.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonCollectionNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonCollectionNpgsqlTest.cs index 8bb50f0817..5b3acf76f2 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonCollectionNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonCollectionNpgsqlTest.cs @@ -177,7 +177,6 @@ public override async Task Index_out_of_bounds() #region GroupBy - [ConditionalFact] public override async Task GroupBy() { await base.GroupBy(); diff --git a/test/EFCore.PG.FunctionalTests/Query/Associations/Navigations/NavigationsCollectionNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Associations/Navigations/NavigationsCollectionNpgsqlTest.cs index 75f696e390..0469b4e45c 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Associations/Navigations/NavigationsCollectionNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Associations/Navigations/NavigationsCollectionNpgsqlTest.cs @@ -210,7 +210,6 @@ public override async Task Index_out_of_bounds() #region GroupBy - [ConditionalFact] public override async Task GroupBy() { await base.GroupBy(); diff --git a/test/EFCore.PG.FunctionalTests/Query/Associations/OwnedJson/OwnedJsonCollectionNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Associations/OwnedJson/OwnedJsonCollectionNpgsqlTest.cs index 855fdbb008..649da461f7 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Associations/OwnedJson/OwnedJsonCollectionNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Associations/OwnedJson/OwnedJsonCollectionNpgsqlTest.cs @@ -191,7 +191,6 @@ public override async Task Index_out_of_bounds() #region GroupBy - [ConditionalFact] public override async Task GroupBy() { await base.GroupBy(); diff --git a/test/EFCore.PG.FunctionalTests/Query/Associations/OwnedNavigations/OwnedNavigationsCollectionNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Associations/OwnedNavigations/OwnedNavigationsCollectionNpgsqlTest.cs index 0d768c9456..1c0859e643 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Associations/OwnedNavigations/OwnedNavigationsCollectionNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Associations/OwnedNavigations/OwnedNavigationsCollectionNpgsqlTest.cs @@ -213,7 +213,6 @@ public override async Task Index_out_of_bounds() #region GroupBy - [ConditionalFact] public override async Task GroupBy() { await base.GroupBy(); diff --git a/test/EFCore.PG.FunctionalTests/Query/CompatibilityQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/CompatibilityQueryNpgsqlTest.cs index 2de51d50c1..a4d0608641 100644 --- a/test/EFCore.PG.FunctionalTests/Query/CompatibilityQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/CompatibilityQueryNpgsqlTest.cs @@ -74,7 +74,7 @@ public virtual CompatibilityContext CreateRedshiftContext() return new CompatibilityContext(builder.Options); } - public virtual async Task InitializeAsync() + public virtual async ValueTask InitializeAsync() { _testStore = NpgsqlTestStoreFactory.Instance.GetOrCreate(StoreName); await _testStore.InitializeAsync(null, CreateContext, c => CompatibilityContext.SeedAsync((CompatibilityContext)c)); @@ -85,7 +85,7 @@ public virtual void Dispose() { } - public virtual async Task DisposeAsync() + public virtual async ValueTask DisposeAsync() => await _testStore.DisposeAsync(); } diff --git a/test/EFCore.PG.FunctionalTests/Query/ComplexNavigationsSharedTypeQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/ComplexNavigationsSharedTypeQueryNpgsqlTest.cs index fc8c7346a2..92f8fd67b9 100644 --- a/test/EFCore.PG.FunctionalTests/Query/ComplexNavigationsSharedTypeQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/ComplexNavigationsSharedTypeQueryNpgsqlTest.cs @@ -15,7 +15,6 @@ public ComplexNavigationsSharedTypeQueryNpgsqlTest( Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } - [ConditionalTheory(Skip = "https://github.com/dotnet/efcore/issues/26353")] public override Task Subquery_with_Distinct_Skip_FirstOrDefault_without_OrderBy(bool async) => base.Subquery_with_Distinct_Skip_FirstOrDefault_without_OrderBy(async); @@ -30,11 +29,9 @@ public override Task GroupJoin_client_method_in_OrderBy(bool async) "Microsoft.EntityFrameworkCore.Query.ComplexNavigationsQueryTestBase", "ClientMethodNullableInt")); - [ConditionalTheory(Skip = "https://github.com/dotnet/efcore/issues/26104")] public override Task GroupBy_aggregate_where_required_relationship(bool async) => base.GroupBy_aggregate_where_required_relationship(async); - [ConditionalTheory(Skip = "https://github.com/dotnet/efcore/issues/26104")] public override Task GroupBy_aggregate_where_required_relationship_2(bool async) => base.GroupBy_aggregate_where_required_relationship_2(async); } diff --git a/test/EFCore.PG.FunctionalTests/Query/EntitySplittingQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/EntitySplittingQueryNpgsqlTest.cs index f9ff56eb76..e67c090fd4 100644 --- a/test/EFCore.PG.FunctionalTests/Query/EntitySplittingQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/EntitySplittingQueryNpgsqlTest.cs @@ -224,28 +224,49 @@ END AS "OwnedStringValue4" """); } - [ConditionalTheory(Skip = "Issue29075")] public override async Task Normal_entity_owning_a_split_reference_with_main_fragment_not_sharing(bool async) { await base.Normal_entity_owning_a_split_reference_with_main_fragment_not_sharing(async); - AssertSql(); + AssertSql( + """ +SELECT e."Id", e."EntityThreeId", e."IntValue1", e."IntValue2", e."IntValue3", e."IntValue4", e."StringValue1", e."StringValue2", e."StringValue3", e."StringValue4", o."EntityOneId", o."Id", o."OwnedIntValue1", o."OwnedIntValue2", o1."OwnedIntValue3", o0."OwnedIntValue4", o."OwnedStringValue1", o."OwnedStringValue2", o1."OwnedStringValue3", o0."OwnedStringValue4" +FROM "EntityOnes" AS e +LEFT JOIN "OwnedReferences" AS o ON e."Id" = o."EntityOneId" +LEFT JOIN "OwnedReferenceExtras2" AS o0 ON o."EntityOneId" = o0."EntityOneId" +LEFT JOIN "OwnedReferenceExtras1" AS o1 ON o."EntityOneId" = o1."EntityOneId" +"""); } - [ConditionalTheory(Skip = "Issue29075")] public override async Task Normal_entity_owning_a_split_reference_with_main_fragment_not_sharing_custom_projection(bool async) { await base.Normal_entity_owning_a_split_reference_with_main_fragment_not_sharing_custom_projection(async); - AssertSql(); + AssertSql( + """ +SELECT e."Id", o0."OwnedIntValue4", o0."OwnedStringValue4" +FROM "EntityOnes" AS e +LEFT JOIN "OwnedReferences" AS o ON e."Id" = o."EntityOneId" +LEFT JOIN "OwnedReferenceExtras2" AS o0 ON o."EntityOneId" = o0."EntityOneId" +"""); } - [ConditionalTheory(Skip = "Issue29075")] public override async Task Normal_entity_owning_a_split_collection(bool async) { await base.Normal_entity_owning_a_split_collection(async); - AssertSql(); + AssertSql( + """ +SELECT e."Id", e."EntityThreeId", e."IntValue1", e."IntValue2", e."IntValue3", e."IntValue4", e."StringValue1", e."StringValue2", e."StringValue3", e."StringValue4", s."EntityOneId", s."Id", s."OwnedIntValue1", s."OwnedIntValue2", s."OwnedIntValue3", s."OwnedIntValue4", s."OwnedStringValue1", s."OwnedStringValue2", s."OwnedStringValue3", s."OwnedStringValue4" +FROM "EntityOnes" AS e +LEFT JOIN ( + SELECT o."EntityOneId", o."Id", o."OwnedIntValue1", o."OwnedIntValue2", o1."OwnedIntValue3", o0."OwnedIntValue4", o."OwnedStringValue1", o."OwnedStringValue2", o1."OwnedStringValue3", o0."OwnedStringValue4" + FROM "OwnedCollection" AS o + INNER JOIN "OwnedCollectionExtras2" AS o0 ON o."EntityOneId" = o0."EntityOneId" AND o."Id" = o0."Id" + INNER JOIN "OwnedCollectionExtras1" AS o1 ON o."EntityOneId" = o1."EntityOneId" AND o."Id" = o1."Id" +) AS s ON e."Id" = s."EntityOneId" +ORDER BY e."Id" NULLS FIRST, s."EntityOneId" NULLS FIRST +"""); } public override async Task Normal_entity_owning_a_split_reference_with_main_fragment_sharing_multiple_level(bool async) @@ -291,20 +312,40 @@ public override async Task Split_entity_owning_a_collection(bool async) """); } - [ConditionalTheory(Skip = "Issue29075")] public override async Task Split_entity_owning_a_split_reference_without_table_sharing(bool async) { await base.Split_entity_owning_a_split_reference_without_table_sharing(async); - AssertSql(); + AssertSql( + """ +SELECT e."Id", e."EntityThreeId", e."IntValue1", e."IntValue2", s0."IntValue3", s."IntValue4", e."StringValue1", e."StringValue2", s0."StringValue3", s."StringValue4", o."EntityOneId", o."Id", o."OwnedIntValue1", o."OwnedIntValue2", o1."OwnedIntValue3", o0."OwnedIntValue4", o."OwnedStringValue1", o."OwnedStringValue2", o1."OwnedStringValue3", o0."OwnedStringValue4" +FROM "EntityOne" AS e +INNER JOIN "SplitEntityOnePart3" AS s ON e."Id" = s."Id" +INNER JOIN "SplitEntityOnePart2" AS s0 ON e."Id" = s0."Id" +LEFT JOIN "OwnedReferences" AS o ON e."Id" = o."EntityOneId" +LEFT JOIN "OwnedReferenceExtras2" AS o0 ON o."EntityOneId" = o0."EntityOneId" +LEFT JOIN "OwnedReferenceExtras1" AS o1 ON o."EntityOneId" = o1."EntityOneId" +"""); } - [ConditionalTheory(Skip = "Issue29075")] public override async Task Split_entity_owning_a_split_collection(bool async) { await base.Split_entity_owning_a_split_collection(async); - AssertSql(); + AssertSql( + """ +SELECT e."Id", e."EntityThreeId", e."IntValue1", e."IntValue2", s0."IntValue3", s."IntValue4", e."StringValue1", e."StringValue2", s0."StringValue3", s."StringValue4", s1."EntityOneId", s1."Id", s1."OwnedIntValue1", s1."OwnedIntValue2", s1."OwnedIntValue3", s1."OwnedIntValue4", s1."OwnedStringValue1", s1."OwnedStringValue2", s1."OwnedStringValue3", s1."OwnedStringValue4" +FROM "EntityOne" AS e +INNER JOIN "SplitEntityOnePart3" AS s ON e."Id" = s."Id" +INNER JOIN "SplitEntityOnePart2" AS s0 ON e."Id" = s0."Id" +LEFT JOIN ( + SELECT o."EntityOneId", o."Id", o."OwnedIntValue1", o."OwnedIntValue2", o1."OwnedIntValue3", o0."OwnedIntValue4", o."OwnedStringValue1", o."OwnedStringValue2", o1."OwnedStringValue3", o0."OwnedStringValue4" + FROM "OwnedCollection" AS o + INNER JOIN "OwnedCollectionExtras2" AS o0 ON o."EntityOneId" = o0."EntityOneId" AND o."Id" = o0."Id" + INNER JOIN "OwnedCollectionExtras1" AS o1 ON o."EntityOneId" = o1."EntityOneId" AND o."Id" = o1."Id" +) AS s1 ON e."Id" = s1."EntityOneId" +ORDER BY e."Id" NULLS FIRST, s1."EntityOneId" NULLS FIRST +"""); } public override async Task Split_entity_owning_a_split_reference_with_table_sharing_1(bool async) @@ -334,7 +375,6 @@ public override async Task Split_entity_owning_a_split_reference_with_table_shar """); } - [ConditionalTheory(Skip = "Issue29075")] public override async Task Split_entity_owning_a_split_reference_with_table_sharing_6(bool async) { await base.Split_entity_owning_a_split_reference_with_table_sharing_6(async); @@ -562,20 +602,39 @@ public override async Task Tpc_entity_owning_a_split_reference_on_leaf_with_tabl """); } - [ConditionalTheory(Skip = "Issue29075")] public override async Task Tph_entity_owning_a_split_reference_on_base_without_table_sharing(bool async) { await base.Tph_entity_owning_a_split_reference_on_base_without_table_sharing(async); - AssertSql(); + AssertSql( + """ +SELECT b."Id", b."BaseValue", b."Discriminator", b."MiddleValue", b."SiblingValue", b."LeafValue", o."BaseEntityId", o."Id", o."OwnedIntValue1", o."OwnedIntValue2", o1."OwnedIntValue3", o0."OwnedIntValue4", o."OwnedStringValue1", o."OwnedStringValue2", o1."OwnedStringValue3", o0."OwnedStringValue4" +FROM "BaseEntity" AS b +LEFT JOIN "OwnedReferencePart1" AS o ON b."Id" = o."BaseEntityId" +LEFT JOIN "OwnedReferencePart4" AS o0 ON o."BaseEntityId" = o0."BaseEntityId" +LEFT JOIN "OwnedReferencePart3" AS o1 ON o."BaseEntityId" = o1."BaseEntityId" +"""); } - [ConditionalTheory(Skip = "Issue29075")] public override async Task Tpt_entity_owning_a_split_reference_on_base_without_table_sharing(bool async) { await base.Tpt_entity_owning_a_split_reference_on_base_without_table_sharing(async); - AssertSql(); + AssertSql( + """ +SELECT b."Id", b."BaseValue", m."MiddleValue", s."SiblingValue", l."LeafValue", CASE + WHEN l."Id" IS NOT NULL THEN 'LeafEntity' + WHEN s."Id" IS NOT NULL THEN 'SiblingEntity' + WHEN m."Id" IS NOT NULL THEN 'MiddleEntity' +END AS "Discriminator", o."BaseEntityId", o."Id", o."OwnedIntValue1", o."OwnedIntValue2", o1."OwnedIntValue3", o0."OwnedIntValue4", o."OwnedStringValue1", o."OwnedStringValue2", o1."OwnedStringValue3", o0."OwnedStringValue4" +FROM "BaseEntity" AS b +LEFT JOIN "MiddleEntity" AS m ON b."Id" = m."Id" +LEFT JOIN "SiblingEntity" AS s ON b."Id" = s."Id" +LEFT JOIN "LeafEntity" AS l ON b."Id" = l."Id" +LEFT JOIN "OwnedReferencePart1" AS o ON b."Id" = o."BaseEntityId" +LEFT JOIN "OwnedReferencePart4" AS o0 ON o."BaseEntityId" = o0."BaseEntityId" +LEFT JOIN "OwnedReferencePart3" AS o1 ON o."BaseEntityId" = o1."BaseEntityId" +"""); } public override async Task Tpc_entity_owning_a_split_reference_on_base_without_table_sharing(bool async) @@ -604,20 +663,39 @@ UNION ALL """); } - [ConditionalTheory(Skip = "Issue29075")] public override async Task Tph_entity_owning_a_split_reference_on_middle_without_table_sharing(bool async) { await base.Tph_entity_owning_a_split_reference_on_middle_without_table_sharing(async); - AssertSql(); + AssertSql( + """ +SELECT b."Id", b."BaseValue", b."Discriminator", b."MiddleValue", b."SiblingValue", b."LeafValue", o."MiddleEntityId", o."Id", o."OwnedIntValue1", o."OwnedIntValue2", o1."OwnedIntValue3", o0."OwnedIntValue4", o."OwnedStringValue1", o."OwnedStringValue2", o1."OwnedStringValue3", o0."OwnedStringValue4" +FROM "BaseEntity" AS b +LEFT JOIN "OwnedReferencePart1" AS o ON b."Id" = o."MiddleEntityId" +LEFT JOIN "OwnedReferencePart4" AS o0 ON o."MiddleEntityId" = o0."MiddleEntityId" +LEFT JOIN "OwnedReferencePart3" AS o1 ON o."MiddleEntityId" = o1."MiddleEntityId" +"""); } - [ConditionalTheory(Skip = "Issue29075")] public override async Task Tpt_entity_owning_a_split_reference_on_middle_without_table_sharing(bool async) { await base.Tpt_entity_owning_a_split_reference_on_middle_without_table_sharing(async); - AssertSql(); + AssertSql( + """ +SELECT b."Id", b."BaseValue", m."MiddleValue", s."SiblingValue", l."LeafValue", CASE + WHEN l."Id" IS NOT NULL THEN 'LeafEntity' + WHEN s."Id" IS NOT NULL THEN 'SiblingEntity' + WHEN m."Id" IS NOT NULL THEN 'MiddleEntity' +END AS "Discriminator", o."MiddleEntityId", o."Id", o."OwnedIntValue1", o."OwnedIntValue2", o1."OwnedIntValue3", o0."OwnedIntValue4", o."OwnedStringValue1", o."OwnedStringValue2", o1."OwnedStringValue3", o0."OwnedStringValue4" +FROM "BaseEntity" AS b +LEFT JOIN "MiddleEntity" AS m ON b."Id" = m."Id" +LEFT JOIN "SiblingEntity" AS s ON b."Id" = s."Id" +LEFT JOIN "LeafEntity" AS l ON b."Id" = l."Id" +LEFT JOIN "OwnedReferencePart1" AS o ON b."Id" = o."MiddleEntityId" +LEFT JOIN "OwnedReferencePart4" AS o0 ON o."MiddleEntityId" = o0."MiddleEntityId" +LEFT JOIN "OwnedReferencePart3" AS o1 ON o."MiddleEntityId" = o1."MiddleEntityId" +"""); } public override async Task Tpc_entity_owning_a_split_reference_on_middle_without_table_sharing(bool async) @@ -646,44 +724,108 @@ UNION ALL """); } - [ConditionalTheory(Skip = "Issue29075")] public override async Task Tph_entity_owning_a_split_reference_on_leaf_without_table_sharing(bool async) { await base.Tph_entity_owning_a_split_reference_on_leaf_without_table_sharing(async); - AssertSql(); + AssertSql( + """ +SELECT b."Id", b."BaseValue", b."Discriminator", b."MiddleValue", b."SiblingValue", b."LeafValue", o."LeafEntityId", o."Id", o."OwnedIntValue1", o."OwnedIntValue2", o1."OwnedIntValue3", o0."OwnedIntValue4", o."OwnedStringValue1", o."OwnedStringValue2", o1."OwnedStringValue3", o0."OwnedStringValue4" +FROM "BaseEntity" AS b +LEFT JOIN "OwnedReferencePart1" AS o ON b."Id" = o."LeafEntityId" +LEFT JOIN "OwnedReferencePart4" AS o0 ON o."LeafEntityId" = o0."LeafEntityId" +LEFT JOIN "OwnedReferencePart3" AS o1 ON o."LeafEntityId" = o1."LeafEntityId" +"""); } - [ConditionalTheory(Skip = "Issue29075")] public override async Task Tpt_entity_owning_a_split_reference_on_leaf_without_table_sharing(bool async) { await base.Tpt_entity_owning_a_split_reference_on_leaf_without_table_sharing(async); - AssertSql(); + AssertSql( + """ +SELECT b."Id", b."BaseValue", m."MiddleValue", s."SiblingValue", l."LeafValue", CASE + WHEN l."Id" IS NOT NULL THEN 'LeafEntity' + WHEN s."Id" IS NOT NULL THEN 'SiblingEntity' + WHEN m."Id" IS NOT NULL THEN 'MiddleEntity' +END AS "Discriminator", o."LeafEntityId", o."Id", o."OwnedIntValue1", o."OwnedIntValue2", o1."OwnedIntValue3", o0."OwnedIntValue4", o."OwnedStringValue1", o."OwnedStringValue2", o1."OwnedStringValue3", o0."OwnedStringValue4" +FROM "BaseEntity" AS b +LEFT JOIN "MiddleEntity" AS m ON b."Id" = m."Id" +LEFT JOIN "SiblingEntity" AS s ON b."Id" = s."Id" +LEFT JOIN "LeafEntity" AS l ON b."Id" = l."Id" +LEFT JOIN "OwnedReferencePart1" AS o ON b."Id" = o."LeafEntityId" +LEFT JOIN "OwnedReferencePart4" AS o0 ON o."LeafEntityId" = o0."LeafEntityId" +LEFT JOIN "OwnedReferencePart3" AS o1 ON o."LeafEntityId" = o1."LeafEntityId" +"""); } - [ConditionalTheory(Skip = "Issue29075")] public override async Task Tpc_entity_owning_a_split_reference_on_leaf_without_table_sharing(bool async) { await base.Tpc_entity_owning_a_split_reference_on_leaf_without_table_sharing(async); - AssertSql(); + AssertSql( + """ +SELECT u."Id", u."BaseValue", u."MiddleValue", u."SiblingValue", u."LeafValue", u."Discriminator", o."LeafEntityId", o."Id", o."OwnedIntValue1", o."OwnedIntValue2", o1."OwnedIntValue3", o0."OwnedIntValue4", o."OwnedStringValue1", o."OwnedStringValue2", o1."OwnedStringValue3", o0."OwnedStringValue4" +FROM ( + SELECT b."Id", b."BaseValue", NULL AS "MiddleValue", NULL::int AS "SiblingValue", NULL::int AS "LeafValue", 'BaseEntity' AS "Discriminator" + FROM "BaseEntity" AS b + UNION ALL + SELECT m."Id", m."BaseValue", m."MiddleValue", NULL AS "SiblingValue", NULL AS "LeafValue", 'MiddleEntity' AS "Discriminator" + FROM "MiddleEntity" AS m + UNION ALL + SELECT s."Id", s."BaseValue", NULL AS "MiddleValue", s."SiblingValue", NULL AS "LeafValue", 'SiblingEntity' AS "Discriminator" + FROM "SiblingEntity" AS s + UNION ALL + SELECT l."Id", l."BaseValue", l."MiddleValue", NULL AS "SiblingValue", l."LeafValue", 'LeafEntity' AS "Discriminator" + FROM "LeafEntity" AS l +) AS u +LEFT JOIN "OwnedReferencePart1" AS o ON u."Id" = o."LeafEntityId" +LEFT JOIN "OwnedReferencePart4" AS o0 ON o."LeafEntityId" = o0."LeafEntityId" +LEFT JOIN "OwnedReferencePart3" AS o1 ON o."LeafEntityId" = o1."LeafEntityId" +"""); } - [ConditionalTheory(Skip = "Issue29075")] public override async Task Tph_entity_owning_a_split_collection_on_base(bool async) { await base.Tph_entity_owning_a_split_collection_on_base(async); - AssertSql(); + AssertSql( + """ +SELECT b."Id", b."BaseValue", b."Discriminator", b."MiddleValue", b."SiblingValue", b."LeafValue", s."BaseEntityId", s."Id", s."OwnedIntValue1", s."OwnedIntValue2", s."OwnedIntValue3", s."OwnedIntValue4", s."OwnedStringValue1", s."OwnedStringValue2", s."OwnedStringValue3", s."OwnedStringValue4" +FROM "BaseEntity" AS b +LEFT JOIN ( + SELECT o."BaseEntityId", o."Id", o."OwnedIntValue1", o."OwnedIntValue2", o1."OwnedIntValue3", o0."OwnedIntValue4", o."OwnedStringValue1", o."OwnedStringValue2", o1."OwnedStringValue3", o0."OwnedStringValue4" + FROM "OwnedReferencePart1" AS o + INNER JOIN "OwnedReferencePart4" AS o0 ON o."BaseEntityId" = o0."BaseEntityId" AND o."Id" = o0."Id" + INNER JOIN "OwnedReferencePart3" AS o1 ON o."BaseEntityId" = o1."BaseEntityId" AND o."Id" = o1."Id" +) AS s ON b."Id" = s."BaseEntityId" +ORDER BY b."Id" NULLS FIRST, s."BaseEntityId" NULLS FIRST +"""); } - [ConditionalTheory(Skip = "Issue29075")] public override async Task Tpt_entity_owning_a_split_collection_on_base(bool async) { await base.Tpt_entity_owning_a_split_collection_on_base(async); - AssertSql(); + AssertSql( + """ +SELECT b."Id", b."BaseValue", m."MiddleValue", s."SiblingValue", l."LeafValue", CASE + WHEN l."Id" IS NOT NULL THEN 'LeafEntity' + WHEN s."Id" IS NOT NULL THEN 'SiblingEntity' + WHEN m."Id" IS NOT NULL THEN 'MiddleEntity' +END AS "Discriminator", s0."BaseEntityId", s0."Id", s0."OwnedIntValue1", s0."OwnedIntValue2", s0."OwnedIntValue3", s0."OwnedIntValue4", s0."OwnedStringValue1", s0."OwnedStringValue2", s0."OwnedStringValue3", s0."OwnedStringValue4" +FROM "BaseEntity" AS b +LEFT JOIN "MiddleEntity" AS m ON b."Id" = m."Id" +LEFT JOIN "SiblingEntity" AS s ON b."Id" = s."Id" +LEFT JOIN "LeafEntity" AS l ON b."Id" = l."Id" +LEFT JOIN ( + SELECT o."BaseEntityId", o."Id", o."OwnedIntValue1", o."OwnedIntValue2", o1."OwnedIntValue3", o0."OwnedIntValue4", o."OwnedStringValue1", o."OwnedStringValue2", o1."OwnedStringValue3", o0."OwnedStringValue4" + FROM "OwnedReferencePart1" AS o + INNER JOIN "OwnedReferencePart4" AS o0 ON o."BaseEntityId" = o0."BaseEntityId" AND o."Id" = o0."Id" + INNER JOIN "OwnedReferencePart3" AS o1 ON o."BaseEntityId" = o1."BaseEntityId" AND o."Id" = o1."Id" +) AS s0 ON b."Id" = s0."BaseEntityId" +ORDER BY b."Id" NULLS FIRST, s0."BaseEntityId" NULLS FIRST +"""); } public override async Task Tpc_entity_owning_a_split_collection_on_base(bool async) @@ -716,20 +858,47 @@ LEFT JOIN ( """); } - [ConditionalTheory(Skip = "Issue29075")] public override async Task Tph_entity_owning_a_split_collection_on_middle(bool async) { await base.Tph_entity_owning_a_split_collection_on_middle(async); - AssertSql(); + AssertSql( + """ +SELECT b."Id", b."BaseValue", b."Discriminator", b."MiddleValue", b."SiblingValue", b."LeafValue", s."MiddleEntityId", s."Id", s."OwnedIntValue1", s."OwnedIntValue2", s."OwnedIntValue3", s."OwnedIntValue4", s."OwnedStringValue1", s."OwnedStringValue2", s."OwnedStringValue3", s."OwnedStringValue4" +FROM "BaseEntity" AS b +LEFT JOIN ( + SELECT o."MiddleEntityId", o."Id", o."OwnedIntValue1", o."OwnedIntValue2", o1."OwnedIntValue3", o0."OwnedIntValue4", o."OwnedStringValue1", o."OwnedStringValue2", o1."OwnedStringValue3", o0."OwnedStringValue4" + FROM "OwnedReferencePart1" AS o + INNER JOIN "OwnedReferencePart4" AS o0 ON o."MiddleEntityId" = o0."MiddleEntityId" AND o."Id" = o0."Id" + INNER JOIN "OwnedReferencePart3" AS o1 ON o."MiddleEntityId" = o1."MiddleEntityId" AND o."Id" = o1."Id" +) AS s ON b."Id" = s."MiddleEntityId" +ORDER BY b."Id" NULLS FIRST, s."MiddleEntityId" NULLS FIRST +"""); } - [ConditionalTheory(Skip = "Issue29075")] public override async Task Tpt_entity_owning_a_split_collection_on_middle(bool async) { await base.Tpt_entity_owning_a_split_collection_on_middle(async); - AssertSql(); + AssertSql( + """ +SELECT b."Id", b."BaseValue", m."MiddleValue", s."SiblingValue", l."LeafValue", CASE + WHEN l."Id" IS NOT NULL THEN 'LeafEntity' + WHEN s."Id" IS NOT NULL THEN 'SiblingEntity' + WHEN m."Id" IS NOT NULL THEN 'MiddleEntity' +END AS "Discriminator", s0."MiddleEntityId", s0."Id", s0."OwnedIntValue1", s0."OwnedIntValue2", s0."OwnedIntValue3", s0."OwnedIntValue4", s0."OwnedStringValue1", s0."OwnedStringValue2", s0."OwnedStringValue3", s0."OwnedStringValue4" +FROM "BaseEntity" AS b +LEFT JOIN "MiddleEntity" AS m ON b."Id" = m."Id" +LEFT JOIN "SiblingEntity" AS s ON b."Id" = s."Id" +LEFT JOIN "LeafEntity" AS l ON b."Id" = l."Id" +LEFT JOIN ( + SELECT o."MiddleEntityId", o."Id", o."OwnedIntValue1", o."OwnedIntValue2", o1."OwnedIntValue3", o0."OwnedIntValue4", o."OwnedStringValue1", o."OwnedStringValue2", o1."OwnedStringValue3", o0."OwnedStringValue4" + FROM "OwnedReferencePart1" AS o + INNER JOIN "OwnedReferencePart4" AS o0 ON o."MiddleEntityId" = o0."MiddleEntityId" AND o."Id" = o0."Id" + INNER JOIN "OwnedReferencePart3" AS o1 ON o."MiddleEntityId" = o1."MiddleEntityId" AND o."Id" = o1."Id" +) AS s0 ON b."Id" = s0."MiddleEntityId" +ORDER BY b."Id" NULLS FIRST, s0."MiddleEntityId" NULLS FIRST +"""); } public override async Task Tpc_entity_owning_a_split_collection_on_middle(bool async) @@ -762,28 +931,77 @@ LEFT JOIN ( """); } - [ConditionalTheory(Skip = "Issue29075")] public override async Task Tph_entity_owning_a_split_collection_on_leaf(bool async) { await base.Tph_entity_owning_a_split_collection_on_leaf(async); - AssertSql(); + AssertSql( + """ +SELECT b."Id", b."BaseValue", b."Discriminator", b."MiddleValue", b."SiblingValue", b."LeafValue", s."LeafEntityId", s."Id", s."OwnedIntValue1", s."OwnedIntValue2", s."OwnedIntValue3", s."OwnedIntValue4", s."OwnedStringValue1", s."OwnedStringValue2", s."OwnedStringValue3", s."OwnedStringValue4" +FROM "BaseEntity" AS b +LEFT JOIN ( + SELECT o."LeafEntityId", o."Id", o."OwnedIntValue1", o."OwnedIntValue2", o1."OwnedIntValue3", o0."OwnedIntValue4", o."OwnedStringValue1", o."OwnedStringValue2", o1."OwnedStringValue3", o0."OwnedStringValue4" + FROM "OwnedReferencePart1" AS o + INNER JOIN "OwnedReferencePart4" AS o0 ON o."LeafEntityId" = o0."LeafEntityId" AND o."Id" = o0."Id" + INNER JOIN "OwnedReferencePart3" AS o1 ON o."LeafEntityId" = o1."LeafEntityId" AND o."Id" = o1."Id" +) AS s ON b."Id" = s."LeafEntityId" +ORDER BY b."Id" NULLS FIRST, s."LeafEntityId" NULLS FIRST +"""); } - [ConditionalTheory(Skip = "Issue29075")] public override async Task Tpt_entity_owning_a_split_collection_on_leaf(bool async) { await base.Tpt_entity_owning_a_split_collection_on_leaf(async); - AssertSql(); + AssertSql( + """ +SELECT b."Id", b."BaseValue", m."MiddleValue", s."SiblingValue", l."LeafValue", CASE + WHEN l."Id" IS NOT NULL THEN 'LeafEntity' + WHEN s."Id" IS NOT NULL THEN 'SiblingEntity' + WHEN m."Id" IS NOT NULL THEN 'MiddleEntity' +END AS "Discriminator", s0."LeafEntityId", s0."Id", s0."OwnedIntValue1", s0."OwnedIntValue2", s0."OwnedIntValue3", s0."OwnedIntValue4", s0."OwnedStringValue1", s0."OwnedStringValue2", s0."OwnedStringValue3", s0."OwnedStringValue4" +FROM "BaseEntity" AS b +LEFT JOIN "MiddleEntity" AS m ON b."Id" = m."Id" +LEFT JOIN "SiblingEntity" AS s ON b."Id" = s."Id" +LEFT JOIN "LeafEntity" AS l ON b."Id" = l."Id" +LEFT JOIN ( + SELECT o."LeafEntityId", o."Id", o."OwnedIntValue1", o."OwnedIntValue2", o1."OwnedIntValue3", o0."OwnedIntValue4", o."OwnedStringValue1", o."OwnedStringValue2", o1."OwnedStringValue3", o0."OwnedStringValue4" + FROM "OwnedReferencePart1" AS o + INNER JOIN "OwnedReferencePart4" AS o0 ON o."LeafEntityId" = o0."LeafEntityId" AND o."Id" = o0."Id" + INNER JOIN "OwnedReferencePart3" AS o1 ON o."LeafEntityId" = o1."LeafEntityId" AND o."Id" = o1."Id" +) AS s0 ON b."Id" = s0."LeafEntityId" +ORDER BY b."Id" NULLS FIRST, s0."LeafEntityId" NULLS FIRST +"""); } - [ConditionalTheory(Skip = "Issue29075")] public override async Task Tpc_entity_owning_a_split_collection_on_leaf(bool async) { await base.Tpc_entity_owning_a_split_collection_on_leaf(async); - AssertSql(); + AssertSql( + """ +SELECT u."Id", u."BaseValue", u."MiddleValue", u."SiblingValue", u."LeafValue", u."Discriminator", s0."LeafEntityId", s0."Id", s0."OwnedIntValue1", s0."OwnedIntValue2", s0."OwnedIntValue3", s0."OwnedIntValue4", s0."OwnedStringValue1", s0."OwnedStringValue2", s0."OwnedStringValue3", s0."OwnedStringValue4" +FROM ( + SELECT b."Id", b."BaseValue", NULL AS "MiddleValue", NULL::int AS "SiblingValue", NULL::int AS "LeafValue", 'BaseEntity' AS "Discriminator" + FROM "BaseEntity" AS b + UNION ALL + SELECT m."Id", m."BaseValue", m."MiddleValue", NULL AS "SiblingValue", NULL AS "LeafValue", 'MiddleEntity' AS "Discriminator" + FROM "MiddleEntity" AS m + UNION ALL + SELECT s."Id", s."BaseValue", NULL AS "MiddleValue", s."SiblingValue", NULL AS "LeafValue", 'SiblingEntity' AS "Discriminator" + FROM "SiblingEntity" AS s + UNION ALL + SELECT l."Id", l."BaseValue", l."MiddleValue", NULL AS "SiblingValue", l."LeafValue", 'LeafEntity' AS "Discriminator" + FROM "LeafEntity" AS l +) AS u +LEFT JOIN ( + SELECT o."LeafEntityId", o."Id", o."OwnedIntValue1", o."OwnedIntValue2", o1."OwnedIntValue3", o0."OwnedIntValue4", o."OwnedStringValue1", o."OwnedStringValue2", o1."OwnedStringValue3", o0."OwnedStringValue4" + FROM "OwnedReferencePart1" AS o + INNER JOIN "OwnedReferencePart4" AS o0 ON o."LeafEntityId" = o0."LeafEntityId" AND o."Id" = o0."Id" + INNER JOIN "OwnedReferencePart3" AS o1 ON o."LeafEntityId" = o1."LeafEntityId" AND o."Id" = o1."Id" +) AS s0 ON u."Id" = s0."LeafEntityId" +ORDER BY u."Id" NULLS FIRST, s0."LeafEntityId" NULLS FIRST +"""); } protected override ITestStoreFactory NonSharedTestStoreFactory diff --git a/test/EFCore.PG.FunctionalTests/Query/FromSqlQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/FromSqlQueryNpgsqlTest.cs index 3e252ec922..6115d8eca3 100644 --- a/test/EFCore.PG.FunctionalTests/Query/FromSqlQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/FromSqlQueryNpgsqlTest.cs @@ -7,13 +7,37 @@ namespace Microsoft.EntityFrameworkCore.Query; public class FromSqlQueryNpgsqlTest(NorthwindQueryNpgsqlFixture fixture) : FromSqlQueryTestBase>(fixture) { - [ConditionalTheory(Skip = "https://github.com/aspnet/EntityFramework/issues/{6563,20364}")] - public override Task Bad_data_error_handling_invalid_cast(bool async) - => base.Bad_data_error_handling_invalid_cast(async); + public override async Task Bad_data_error_handling_invalid_cast(bool async) + { + await using var context = CreateContext(); + var query = context.Set().FromSqlRaw( + NormalizeDelimitersInRawString( + """ +SELECT [ProductID], [SupplierID] AS [UnitPrice], [ProductName], [SupplierID], [UnitsInStock], [Discontinued] +FROM [Products] +""")); + + var products = async ? await query.ToListAsync() : query.ToList(); + + Assert.NotEmpty(products); + } - [ConditionalTheory(Skip = "https://github.com/aspnet/EntityFramework/issues/{6563,20364}")] - public override Task Bad_data_error_handling_invalid_cast_projection(bool async) - => base.Bad_data_error_handling_invalid_cast_projection(async); + public override async Task Bad_data_error_handling_invalid_cast_projection(bool async) + { + await using var context = CreateContext(); + var query = context.Set() + .FromSqlRaw( + NormalizeDelimitersInRawString( + """ +SELECT [ProductID], [SupplierID] AS [UnitPrice], [ProductName], [UnitsInStock], [Discontinued] +FROM [Products] +""")) + .Select(p => p.UnitPrice); + + var unitPrices = async ? await query.ToListAsync() : query.ToList(); + + Assert.NotEmpty(unitPrices); + } // The test attempts to project out a column with the wrong case; this works on other databases, and fails when EF tries to materialize. // But in PG this fails at the database since PG is case-sensitive and the column does not exist. @@ -66,8 +90,6 @@ from o in context.Set().FromSql( Assert.Single(actual); } - [ConditionalTheory] - [MemberData(nameof(IsAsyncData))] public override async Task FromSql_queryable_multiple_composed_with_parameters_and_closure_parameters_interpolated(bool async) { // We default to mapping DateTime to 'timestamp with time zone', but here we need to send `timestamp without time zone` to match diff --git a/test/EFCore.PG.FunctionalTests/Query/Inheritance/TPCInheritanceQueryNpgsqlFixture.cs b/test/EFCore.PG.FunctionalTests/Query/Inheritance/TPCInheritanceQueryNpgsqlFixture.cs index 4b4f763ac2..383ff8eac1 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Inheritance/TPCInheritanceQueryNpgsqlFixture.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Inheritance/TPCInheritanceQueryNpgsqlFixture.cs @@ -1,7 +1,24 @@ +using Microsoft.EntityFrameworkCore.TestModels.InheritanceModel; + namespace Microsoft.EntityFrameworkCore.Query.Inheritance; public class TPCInheritanceQueryNpgsqlFixture : TPCInheritanceQueryFixture { protected override ITestStoreFactory TestStoreFactory => NpgsqlTestStoreFactory.Instance; + + protected override async Task SeedAsync(InheritanceContext context) + { + await base.SeedAsync(context); + + await context.Database.ExecuteSqlRawAsync( + """ +SELECT setval( + '"AnimalSequence"', + GREATEST( + COALESCE((SELECT MAX("Id") FROM "Kiwi"), 1), + COALESCE((SELECT MAX("Id") FROM "Eagle"), 1)), + true); +"""); + } } diff --git a/test/EFCore.PG.FunctionalTests/Query/Inheritance/TPCInheritanceQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Inheritance/TPCInheritanceQueryNpgsqlTest.cs index 427cd8c2a1..22929ce408 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Inheritance/TPCInheritanceQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Inheritance/TPCInheritanceQueryNpgsqlTest.cs @@ -78,12 +78,6 @@ ORDER BY e1."Id" NULLS FIRST """); } - // Seed data for the fixture manually inserts entities with IDs 1, 2; then this test attempts to insert another one with an auto-generated ID, - // but the PG sequence wasn't updated so produces 1, resulting in a conflict. The test should be consistent in either using either - // auto-generated IDs or not across the board. - public override Task Can_insert_update_delete() - => Assert.ThrowsAsync(base.Can_insert_update_delete); - public override async Task Can_query_all_animals(bool async) { await base.Can_query_all_animals(async); diff --git a/test/EFCore.PG.FunctionalTests/Query/LegacyTimestampQueryTest.cs b/test/EFCore.PG.FunctionalTests/Query/LegacyTimestampQueryTest.cs index ad50487189..41d5fdbafa 100644 --- a/test/EFCore.PG.FunctionalTests/Query/LegacyTimestampQueryTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/LegacyTimestampQueryTest.cs @@ -110,10 +110,10 @@ public LegacyTimestampQueryFixture() NpgsqlTypeMappingSource.LegacyTimestampBehavior = true; } - public override Task DisposeAsync() + public override ValueTask DisposeAsync() { NpgsqlTypeMappingSource.LegacyTimestampBehavior = false; - return Task.CompletedTask; + return default; } protected override async Task SeedAsync(TimestampQueryContext context) diff --git a/test/EFCore.PG.FunctionalTests/Query/NorthwindDbFunctionsQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/NorthwindDbFunctionsQueryNpgsqlTest.cs index 64df0f9ec1..71be547c88 100644 --- a/test/EFCore.PG.FunctionalTests/Query/NorthwindDbFunctionsQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/NorthwindDbFunctionsQueryNpgsqlTest.cs @@ -127,7 +127,7 @@ WHERE c."ContactName" NOT ILIKE '%M%' OR c."ContactName" IS NULL #region Collation [MinimumPostgresVersion(12, 0)] - [PlatformSkipCondition(TestUtilities.Xunit.TestPlatform.Windows, SkipReason = "ICU non-deterministic doesn't seem to work on Windows?")] + [SkipOnPlatform(TestPlatforms.Windows, "ICU non-deterministic doesn't seem to work on Windows?")] public override async Task Collate_case_insensitive(bool async) { await base.Collate_case_insensitive(async); diff --git a/test/EFCore.PG.FunctionalTests/Query/NorthwindGroupByQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/NorthwindGroupByQueryNpgsqlTest.cs index 9d83ef072f..0dc0bff9d1 100644 --- a/test/EFCore.PG.FunctionalTests/Query/NorthwindGroupByQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/NorthwindGroupByQueryNpgsqlTest.cs @@ -2031,7 +2031,6 @@ ORDER BY o0."CustomerID" NULLS FIRST """); } - [ConditionalTheory(Skip = "https://github.com/dotnet/efcore/pull/28410")] public override async Task Select_nested_collection_with_groupby(bool async) { await base.Select_nested_collection_with_groupby(async); @@ -2041,14 +2040,14 @@ public override async Task Select_nested_collection_with_groupby(bool async) SELECT EXISTS ( SELECT 1 FROM "Orders" AS o - WHERE c."CustomerID" = o."CustomerID"), c."CustomerID", t."OrderID" + WHERE c."CustomerID" = o."CustomerID"), c."CustomerID", o1."OrderID" FROM "Customers" AS c LEFT JOIN LATERAL ( SELECT o0."OrderID" FROM "Orders" AS o0 WHERE c."CustomerID" = o0."CustomerID" GROUP BY o0."OrderID" -) AS t ON TRUE +) AS o1 ON TRUE WHERE c."CustomerID" LIKE 'F%' ORDER BY c."CustomerID" NULLS FIRST """); diff --git a/test/EFCore.PG.FunctionalTests/Query/PrimitiveCollectionsQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/PrimitiveCollectionsQueryNpgsqlTest.cs index b037bdea1e..ffbaf9ba1c 100644 --- a/test/EFCore.PG.FunctionalTests/Query/PrimitiveCollectionsQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/PrimitiveCollectionsQueryNpgsqlTest.cs @@ -2677,7 +2677,6 @@ WHERE NOT (p."Int" = ANY (@ints) AND p."Int" = ANY (@ints) IS NOT NULL) """); } - [ConditionalFact] public override async Task Multidimensional_array_is_not_supported() { // Multidimensional arrays are supported in PostgreSQL (via the regular array type); the EFCore.PG maps .NET diff --git a/test/EFCore.PG.FunctionalTests/Query/SqlQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/SqlQueryNpgsqlTest.cs index 790c9afa0d..438fd1ba00 100644 --- a/test/EFCore.PG.FunctionalTests/Query/SqlQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/SqlQueryNpgsqlTest.cs @@ -360,7 +360,6 @@ SELECT m."ProductName" """); } - [ConditionalTheory(Skip = "https://github.com/dotnet/efcore/pull/30384")] public override async Task SqlQueryRaw_annotations_do_not_affect_successive_calls(bool async) { await base.SqlQueryRaw_annotations_do_not_affect_successive_calls(async); @@ -460,7 +459,6 @@ public override async Task SqlQuery_with_inlined_db_parameter_without_name_prefi """); } - [ConditionalTheory(Skip = "https://github.com/dotnet/efcore/pull/30384")] public override async Task SqlQuery_parameterization_issue_12213(bool async) { await base.SqlQuery_parameterization_issue_12213(async); @@ -476,35 +474,35 @@ SELECT m."OrderID" """, // """ -@__max_1='10400' +@max='10400' p0='10300' SELECT m."OrderID" FROM ( SELECT * FROM "Orders" ) AS m -WHERE m."OrderID" <= @__max_1 AND EXISTS ( - SELECT 1 +WHERE m."OrderID" <= @max AND m."OrderID" IN ( + SELECT m0."OrderID" FROM ( SELECT * FROM "Orders" WHERE "OrderID" >= @p0 ) AS m0 - WHERE m0."OrderID" = m."OrderID") +) """, // """ -@__max_1='10400' +@max='10400' p0='10300' SELECT m."OrderID" FROM ( SELECT * FROM "Orders" ) AS m -WHERE m."OrderID" <= @__max_1 AND EXISTS ( - SELECT 1 +WHERE m."OrderID" <= @max AND m."OrderID" IN ( + SELECT m0."OrderID" FROM ( SELECT * FROM "Orders" WHERE "OrderID" >= @p0 ) AS m0 - WHERE m0."OrderID" = m."OrderID") +) """); } @@ -672,12 +670,12 @@ WHERE m."ContactName" LIKE '%z%' } #pragma warning disable xUnit1026 - [ConditionalTheory(Skip = "https://github.com/dotnet/efcore/pull/30384")] public override Task Bad_data_error_handling_invalid_cast(bool async) + // Not supported on PostgreSQL: Npgsql successfully materializes the test data instead of throwing. => Task.CompletedTask; - [ConditionalTheory(Skip = "https://github.com/dotnet/efcore/pull/30384")] public override Task Bad_data_error_handling_invalid_cast_projection(bool async) + // Not supported on PostgreSQL: Npgsql successfully materializes the test data instead of throwing. => Task.CompletedTask; #pragma warning restore xUnit1026 diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/ByteArrayTranslationsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/ByteArrayTranslationsNpgsqlTest.cs index 8ec0c43dd7..1dbd9c51c7 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Translations/ByteArrayTranslationsNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/ByteArrayTranslationsNpgsqlTest.cs @@ -100,7 +100,6 @@ public override async Task SequenceEqual() """); } - [ConditionalFact] public override async Task Any() { await AssertQuery(ss => ss.Set().Where(e => e.ByteArray.Any())); diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/MathTranslationsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/MathTranslationsNpgsqlTest.cs index 36a6960e04..a410dc72a4 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Translations/MathTranslationsNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/MathTranslationsNpgsqlTest.cs @@ -427,6 +427,11 @@ public override async Task Sign() SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" FROM "BasicTypesEntities" AS b WHERE sign(b."Double")::int > 0 +""", + // + """ +SELECT sign(b."Double")::int +FROM "BasicTypesEntities" AS b """); } @@ -439,6 +444,11 @@ public override async Task Sign_float() SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" FROM "BasicTypesEntities" AS b WHERE sign(b."Float")::int > 0 +""", + // + """ +SELECT sign(b."Float")::int +FROM "BasicTypesEntities" AS b """); } diff --git a/test/EFCore.PG.FunctionalTests/Query/UdfDbFunctionNpgsqlTests.cs b/test/EFCore.PG.FunctionalTests/Query/UdfDbFunctionNpgsqlTests.cs index 5626e5c8d2..b45c008637 100644 --- a/test/EFCore.PG.FunctionalTests/Query/UdfDbFunctionNpgsqlTests.cs +++ b/test/EFCore.PG.FunctionalTests/Query/UdfDbFunctionNpgsqlTests.cs @@ -584,7 +584,6 @@ LIMIT 2 #endregion #if RELEASE - [ConditionalFact(Skip = "https://github.com/dotnet/efcore/pull/30388")] public override void Scalar_Function_with_nullable_value_return_type_throws() {} #endif diff --git a/test/EFCore.PG.FunctionalTests/Scaffolding/NpgsqlDatabaseModelFactoryTest.cs b/test/EFCore.PG.FunctionalTests/Scaffolding/NpgsqlDatabaseModelFactoryTest.cs index 7cdfe3d885..faa5c1d666 100644 --- a/test/EFCore.PG.FunctionalTests/Scaffolding/NpgsqlDatabaseModelFactoryTest.cs +++ b/test/EFCore.PG.FunctionalTests/Scaffolding/NpgsqlDatabaseModelFactoryTest.cs @@ -2214,7 +2214,7 @@ protected override ITestStoreFactory TestStoreFactory public new NpgsqlTestStore TestStore => (NpgsqlTestStore)base.TestStore; - public override async Task InitializeAsync() + public override async ValueTask InitializeAsync() { await base.InitializeAsync(); await TestStore.ExecuteNonQueryAsync("CREATE SCHEMA IF NOT EXISTS db2"); diff --git a/test/EFCore.PG.FunctionalTests/SequenceEndToEndTest.cs b/test/EFCore.PG.FunctionalTests/SequenceEndToEndTest.cs index 7fea096121..918f1a655d 100644 --- a/test/EFCore.PG.FunctionalTests/SequenceEndToEndTest.cs +++ b/test/EFCore.PG.FunctionalTests/SequenceEndToEndTest.cs @@ -391,9 +391,9 @@ private class Unicon protected NpgsqlTestStore TestStore { get; private set; } = null!; - public async Task InitializeAsync() + public async ValueTask InitializeAsync() => TestStore = await NpgsqlTestStore.CreateInitializedAsync("SequenceEndToEndTest"); - public async Task DisposeAsync() + public async ValueTask DisposeAsync() => await TestStore.DisposeAsync(); } diff --git a/test/EFCore.PG.FunctionalTests/TestUtilities/MinimumPostgresVersionAttribute.cs b/test/EFCore.PG.FunctionalTests/TestUtilities/MinimumPostgresVersionAttribute.cs index 134331d743..de241f2fc8 100644 --- a/test/EFCore.PG.FunctionalTests/TestUtilities/MinimumPostgresVersionAttribute.cs +++ b/test/EFCore.PG.FunctionalTests/TestUtilities/MinimumPostgresVersionAttribute.cs @@ -1,13 +1,15 @@ namespace Microsoft.EntityFrameworkCore.TestUtilities; +/// +/// Marks tests as requiring a minimum PostgreSQL version. When the configured PostgreSQL version +/// is older, the attribute contributes a category=failing trait so that the test runner can +/// exclude the affected tests. +/// [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] -public sealed class MinimumPostgresVersionAttribute(int major, int minor) : Attribute, ITestCondition +public sealed class MinimumPostgresVersionAttribute(int major, int minor) : Attribute, global::Xunit.v3.ITraitAttribute { private readonly Version _version = new(major, minor); - public ValueTask IsMetAsync() - => new(TestEnvironment.PostgresVersion >= _version); - - public string SkipReason - => $"Requires PostgreSQL version {_version} or later."; + public IReadOnlyCollection> GetTraits() + => TestEnvironment.PostgresVersion >= _version ? [] : [new KeyValuePair("category", "failing")]; } diff --git a/test/EFCore.PG.FunctionalTests/TestUtilities/RequiresPostgisAttribute.cs b/test/EFCore.PG.FunctionalTests/TestUtilities/RequiresPostgisAttribute.cs index 5035dc4657..b48c9daa11 100644 --- a/test/EFCore.PG.FunctionalTests/TestUtilities/RequiresPostgisAttribute.cs +++ b/test/EFCore.PG.FunctionalTests/TestUtilities/RequiresPostgisAttribute.cs @@ -2,12 +2,15 @@ namespace Microsoft.EntityFrameworkCore.TestUtilities; +/// +/// Marks tests as requiring PostGIS. When PostGIS is not available, the attribute contributes a +/// category=failing trait so that the test runner can exclude the affected tests. +/// [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] -public sealed class RequiresPostgisAttribute : Attribute, ITestCondition +public sealed class RequiresPostgisAttribute : Attribute, global::Xunit.v3.ITraitAttribute { - public ValueTask IsMetAsync() - => new(TestEnvironment.IsPostgisAvailable || Environment.GetEnvironmentVariable("NPGSQL_TEST_POSTGIS")?.ToLower(CultureInfo.InvariantCulture) is "1" or "true"); - - public string SkipReason - => "PostGIS isn't installed, skipping"; + public IReadOnlyCollection> GetTraits() + => TestEnvironment.IsPostgisAvailable || Environment.GetEnvironmentVariable("NPGSQL_TEST_POSTGIS")?.ToLower(CultureInfo.InvariantCulture) is "1" or "true" + ? [] + : [new KeyValuePair("category", "failing")]; } diff --git a/test/EFCore.PG.FunctionalTests/Update/JsonUpdateNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Update/JsonUpdateNpgsqlTest.cs index eeb8abaa76..053aef053d 100644 --- a/test/EFCore.PG.FunctionalTests/Update/JsonUpdateNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Update/JsonUpdateNpgsqlTest.cs @@ -1255,7 +1255,6 @@ LIMIT 2 """); } - [ConditionalFact] public override async Task Edit_single_property_with_converter_string_True_False_to_bool() { await base.Edit_single_property_with_converter_string_True_False_to_bool(); @@ -1277,7 +1276,6 @@ LIMIT 2 """); } - [ConditionalFact] public override async Task Edit_single_property_with_converter_string_Y_N_to_bool() { await base.Edit_single_property_with_converter_string_Y_N_to_bool(); diff --git a/test/EFCore.PG.FunctionalTests/ValueConvertersEndToEndNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/ValueConvertersEndToEndNpgsqlTest.cs index 7fa28f4e15..1707403047 100644 --- a/test/EFCore.PG.FunctionalTests/ValueConvertersEndToEndNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/ValueConvertersEndToEndNpgsqlTest.cs @@ -3,7 +3,6 @@ namespace Microsoft.EntityFrameworkCore; public class ValueConvertersEndToEndNpgsqlTest(ValueConvertersEndToEndNpgsqlTest.ValueConvertersEndToEndNpgsqlFixture fixture) : ValueConvertersEndToEndTestBase(fixture) { - [ConditionalTheory(Skip = "DateTime and DateTimeOffset, https://github.com/dotnet/efcore/issues/26068")] public override Task Can_insert_and_read_back_with_conversions(int[] valueOrder) => base.Can_insert_and_read_back_with_conversions(valueOrder); @@ -54,8 +53,8 @@ public override Task Can_insert_and_read_back_with_conversions(int[] valueOrder) [InlineData(nameof(ConvertingEntity.StringToNullableBytes), "bytea", false)] [InlineData(nameof(ConvertingEntity.StringToChar), "character(1)", false)] [InlineData(nameof(ConvertingEntity.StringToNullableChar), "character(1)", false)] - [InlineData(nameof(ConvertingEntity.StringToDateTime), "timestamp with time zone", false)] - [InlineData(nameof(ConvertingEntity.StringToNullableDateTime), "timestamp with time zone", false)] + [InlineData(nameof(ConvertingEntity.StringToDateTime), "timestamp without time zone", false)] + [InlineData(nameof(ConvertingEntity.StringToNullableDateTime), "timestamp without time zone", false)] // [InlineData(nameof(ConvertingEntity.StringToDateTimeOffset), "timestamp with time zone", false)] // [InlineData(nameof(ConvertingEntity.StringToNullableDateTimeOffset), "timestamp with time zone", false)] [InlineData(nameof(ConvertingEntity.StringToEnum), "integer", false)] @@ -118,8 +117,8 @@ public override Task Can_insert_and_read_back_with_conversions(int[] valueOrder) [InlineData(nameof(ConvertingEntity.NullableStringToNullableBytes), "bytea", true)] [InlineData(nameof(ConvertingEntity.NullableStringToChar), "character(1)", true)] [InlineData(nameof(ConvertingEntity.NullableStringToNullableChar), "character(1)", true)] - [InlineData(nameof(ConvertingEntity.NullableStringToDateTime), "timestamp with time zone", true)] - [InlineData(nameof(ConvertingEntity.NullableStringToNullableDateTime), "timestamp with time zone", true)] + [InlineData(nameof(ConvertingEntity.NullableStringToDateTime), "timestamp without time zone", true)] + [InlineData(nameof(ConvertingEntity.NullableStringToNullableDateTime), "timestamp without time zone", true)] //[InlineData(nameof(ConvertingEntity.NullableStringToDateTimeOffset), "timestamp with time zone", true)] //[InlineData(nameof(ConvertingEntity.NullableStringToNullableDateTimeOffset), "timestamp with time zone", true)] [InlineData(nameof(ConvertingEntity.NullableStringToEnum), "integer", true)] @@ -186,6 +185,11 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con b.Ignore(e => e.StringToNullableDateTimeOffset); b.Ignore(e => e.NullableStringToDateTimeOffset); b.Ignore(e => e.NullableStringToNullableDateTimeOffset); + + b.Property(e => e.StringToDateTime).HasColumnType("timestamp without time zone"); + b.Property(e => e.StringToNullableDateTime).HasColumnType("timestamp without time zone"); + b.Property(e => e.NullableStringToDateTime).HasColumnType("timestamp without time zone"); + b.Property(e => e.NullableStringToNullableDateTime).HasColumnType("timestamp without time zone"); }); // Add some Npgsql-specific value conversion scenarios