diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8c6d4bc99..242f8e6f6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ on: pull_request: env: - dotnet_sdk_version: '9.0.100-preview.7.24407.12' + dotnet_sdk_version: '9.0.100-rc.1.24451.4' postgis_version: 3 DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 9f6bdb73b..31920ab09 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -27,7 +27,7 @@ on: - cron: '30 22 * * 6' env: - dotnet_sdk_version: '9.0.100-preview.7.24407.12' + dotnet_sdk_version: '9.0.100-rc.1.24451.4' jobs: analyze: diff --git a/Directory.Packages.props b/Directory.Packages.props index 719cf3228..fd299fb25 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,7 +1,7 @@ - [9.0.0-preview.7.24405.3] - 9.0.0-preview.7.24405.7 + [9.0.0-rc.1.24451.1] + 9.0.0-rc.1.24431.7 8.0.3 @@ -21,15 +21,16 @@ - + + - - - - + + + + diff --git a/global.json b/global.json index a143424dc..8fcf651cb 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.100-preview.7.24407.12", + "version": "9.0.100-rc.1.24451.4", "rollForward": "latestMajor", "allowPrerelease": true } diff --git a/src/EFCore.PG/Migrations/Internal/NpgsqlHistoryRepository.cs b/src/EFCore.PG/Migrations/Internal/NpgsqlHistoryRepository.cs index beaf86d7c..5fadf3eba 100644 --- a/src/EFCore.PG/Migrations/Internal/NpgsqlHistoryRepository.cs +++ b/src/EFCore.PG/Migrations/Internal/NpgsqlHistoryRepository.cs @@ -63,7 +63,7 @@ await Dependencies.RawSqlCommandBuilder.Build(ExistsSql).ExecuteScalarAsync( /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public override IDisposable GetDatabaseLock(TimeSpan timeout) + public override IDisposable GetDatabaseLock() { // TODO: There are issues with the current lock implementation in EF - most importantly, the lock isn't acquired within a // transaction so we can't use e.g. LOCK TABLE. This should be fixed for rc.1, see #34439. @@ -81,7 +81,7 @@ public override IDisposable GetDatabaseLock(TimeSpan timeout) /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public override Task GetDatabaseLockAsync(TimeSpan timeout, CancellationToken cancellationToken = default) + public override Task GetDatabaseLockAsync(CancellationToken cancellationToken = default) { // TODO: There are issues with the current lock implementation in EF - most importantly, the lock isn't acquired within a // transaction so we can't use e.g. LOCK TABLE. This should be fixed for rc.1, see #34439. diff --git a/src/EFCore.PG/Migrations/Internal/NpgsqlMigrator.cs b/src/EFCore.PG/Migrations/Internal/NpgsqlMigrator.cs index 6b7d1f16b..cf8d222c5 100644 --- a/src/EFCore.PG/Migrations/Internal/NpgsqlMigrator.cs +++ b/src/EFCore.PG/Migrations/Internal/NpgsqlMigrator.cs @@ -36,10 +36,13 @@ public NpgsqlMigrator( IModelRuntimeInitializer modelRuntimeInitializer, IDiagnosticsLogger logger, IRelationalCommandDiagnosticsLogger commandLogger, - IDatabaseProvider databaseProvider) + IDatabaseProvider databaseProvider, + IMigrationsModelDiffer migrationsModelDiffer, + IDesignTimeModel designTimeModel, + IDbContextOptions contextOptions) : base(migrationsAssembly, historyRepository, databaseCreator, migrationsSqlGenerator, rawSqlCommandBuilder, migrationCommandExecutor, connection, sqlGenerationHelper, currentContext, modelRuntimeInitializer, logger, - commandLogger, databaseProvider) + commandLogger, databaseProvider, migrationsModelDiffer, designTimeModel, contextOptions) { _historyRepository = historyRepository; _connection = connection; @@ -51,7 +54,7 @@ public NpgsqlMigrator( /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public override void Migrate(string? targetMigration = null) + public override void Migrate(string? targetMigration) { var appliedMigrations = _historyRepository.GetAppliedMigrations(); @@ -60,17 +63,15 @@ public override void Migrate(string? targetMigration = null) PopulateMigrations( appliedMigrations.Select(t => t.MigrationId), targetMigration, - out var migrationsToApply, - out var migrationsToRevert, - out _); + out var migratorData); - if (migrationsToRevert.Count + migrationsToApply.Count == 0) + if (migratorData.RevertedMigrations.Count + migratorData.AppliedMigrations.Count == 0) { return; } // If a PostgreSQL extension, enum or range was added, we want Npgsql to reload all types at the ADO.NET level. - var migrations = migrationsToApply.Count > 0 ? migrationsToApply : migrationsToRevert; + var migrations = migratorData.AppliedMigrations.Count > 0 ? migratorData.AppliedMigrations : migratorData.RevertedMigrations; var reloadTypes = migrations .SelectMany(m => m.UpOperations) .OfType() @@ -96,9 +97,7 @@ public override void Migrate(string? targetMigration = null) /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public override async Task MigrateAsync( - string? targetMigration = null, - CancellationToken cancellationToken = default) + public override async Task MigrateAsync(string? targetMigration, CancellationToken cancellationToken = default) { var appliedMigrations = await _historyRepository.GetAppliedMigrationsAsync(cancellationToken).ConfigureAwait(false); @@ -107,17 +106,15 @@ public override async Task MigrateAsync( PopulateMigrations( appliedMigrations.Select(t => t.MigrationId), targetMigration, - out var migrationsToApply, - out var migrationsToRevert, - out _); + out var migratorData); - if (migrationsToRevert.Count + migrationsToApply.Count == 0) + if (migratorData.RevertedMigrations.Count + migratorData.AppliedMigrations.Count == 0) { return; } // If a PostgreSQL extension, enum or range was added, we want Npgsql to reload all types at the ADO.NET level. - var migrations = migrationsToApply.Count > 0 ? migrationsToApply : migrationsToRevert; + var migrations = migratorData.AppliedMigrations.Count > 0 ? migratorData.AppliedMigrations : migratorData.RevertedMigrations; var reloadTypes = migrations .SelectMany(m => m.UpOperations) .OfType() diff --git a/src/EFCore.PG/Query/Internal/NpgsqlParameterBasedSqlProcessor.cs b/src/EFCore.PG/Query/Internal/NpgsqlParameterBasedSqlProcessor.cs index c79480a72..dbb97a5c4 100644 --- a/src/EFCore.PG/Query/Internal/NpgsqlParameterBasedSqlProcessor.cs +++ b/src/EFCore.PG/Query/Internal/NpgsqlParameterBasedSqlProcessor.cs @@ -16,8 +16,8 @@ public class NpgsqlParameterBasedSqlProcessor : RelationalParameterBasedSqlProce /// public NpgsqlParameterBasedSqlProcessor( RelationalParameterBasedSqlProcessorDependencies dependencies, - bool useRelationalNulls) - : base(dependencies, useRelationalNulls) + RelationalParameterBasedSqlProcessorParameters parameters) + : base(dependencies, parameters) { } @@ -48,7 +48,7 @@ protected override Expression ProcessSqlNullability( Check.NotNull(selectExpression, nameof(selectExpression)); Check.NotNull(parametersValues, nameof(parametersValues)); - return new NpgsqlSqlNullabilityProcessor(Dependencies, UseRelationalNulls).Process( + return new NpgsqlSqlNullabilityProcessor(Dependencies, Parameters).Process( selectExpression, parametersValues, out canCache); } } diff --git a/src/EFCore.PG/Query/Internal/NpgsqlParameterBasedSqlProcessorFactory.cs b/src/EFCore.PG/Query/Internal/NpgsqlParameterBasedSqlProcessorFactory.cs index d3d0d342d..fa467d127 100644 --- a/src/EFCore.PG/Query/Internal/NpgsqlParameterBasedSqlProcessorFactory.cs +++ b/src/EFCore.PG/Query/Internal/NpgsqlParameterBasedSqlProcessorFactory.cs @@ -23,11 +23,10 @@ public NpgsqlParameterBasedSqlProcessorFactory( } /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// Creates a new . /// - public virtual RelationalParameterBasedSqlProcessor Create(bool useRelationalNulls) - => new NpgsqlParameterBasedSqlProcessor(_dependencies, useRelationalNulls); + /// Parameters for . + /// A relational parameter based sql processor. + public RelationalParameterBasedSqlProcessor Create(RelationalParameterBasedSqlProcessorParameters parameters) + => new NpgsqlParameterBasedSqlProcessor(_dependencies, parameters); } diff --git a/src/EFCore.PG/Query/Internal/NpgsqlQuerySqlGenerator.cs b/src/EFCore.PG/Query/Internal/NpgsqlQuerySqlGenerator.cs index e81ebcf0a..a6d4a6871 100644 --- a/src/EFCore.PG/Query/Internal/NpgsqlQuerySqlGenerator.cs +++ b/src/EFCore.PG/Query/Internal/NpgsqlQuerySqlGenerator.cs @@ -767,6 +767,11 @@ protected override Expression VisitValues(ValuesExpression valuesExpression) /// protected override void GenerateValues(ValuesExpression valuesExpression) { + if (valuesExpression.RowValues is null) + { + throw new UnreachableException(); + } + if (valuesExpression.RowValues.Count == 0) { throw new InvalidOperationException(RelationalStrings.EmptyCollectionNotSupportedAsInlineQueryRoot); diff --git a/src/EFCore.PG/Query/Internal/NpgsqlSqlNullabilityProcessor.cs b/src/EFCore.PG/Query/Internal/NpgsqlSqlNullabilityProcessor.cs index 09644eb44..215219f19 100644 --- a/src/EFCore.PG/Query/Internal/NpgsqlSqlNullabilityProcessor.cs +++ b/src/EFCore.PG/Query/Internal/NpgsqlSqlNullabilityProcessor.cs @@ -11,14 +11,15 @@ public class NpgsqlSqlNullabilityProcessor : SqlNullabilityProcessor private readonly ISqlExpressionFactory _sqlExpressionFactory; /// - /// Creates a new instance of the class. + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - /// Parameter object containing dependencies for this class. - /// A bool value indicating whether relational null semantics are in use. public NpgsqlSqlNullabilityProcessor( RelationalParameterBasedSqlProcessorDependencies dependencies, - bool useRelationalNulls) - : base(dependencies, useRelationalNulls) + RelationalParameterBasedSqlProcessorParameters parameters) + : base(dependencies, parameters) { _sqlExpressionFactory = dependencies.SqlExpressionFactory; } @@ -101,7 +102,7 @@ SqlExpression VisitRowValueComparison( // visit that (that adds the compensation). We then chain all such expressions together with AND. var valueBinaryExpression = Visit( _sqlExpressionFactory.MakeBinary( - operatorType, visitedLeftValue, visitedRightValue, typeMapping: null, existingExpr: sqlBinaryExpression)!, + operatorType, visitedLeftValue, visitedRightValue, typeMapping: null, existingExpression: sqlBinaryExpression)!, allowOptimizedExpansion, out _); @@ -144,7 +145,7 @@ visitedRightValues is null ? rightRowValue : new PgRowValueExpression(visitedRightValues, leftRowValue.Type, leftRowValue.TypeMapping), typeMapping: null, - existingExpr: sqlBinaryExpression)!; + existingExpression: sqlBinaryExpression)!; } Check.DebugAssert(visitedLeftValues is not null, "visitedLeftValues is not null"); diff --git a/test/EFCore.PG.FunctionalTests/EFCore.PG.FunctionalTests.csproj b/test/EFCore.PG.FunctionalTests/EFCore.PG.FunctionalTests.csproj index 084ec149f..4011ace02 100644 --- a/test/EFCore.PG.FunctionalTests/EFCore.PG.FunctionalTests.csproj +++ b/test/EFCore.PG.FunctionalTests/EFCore.PG.FunctionalTests.csproj @@ -21,6 +21,7 @@ + diff --git a/test/EFCore.PG.FunctionalTests/ExecutionStrategyTest.cs b/test/EFCore.PG.FunctionalTests/ExecutionStrategyTest.cs index f66ac9433..640b3c356 100644 --- a/test/EFCore.PG.FunctionalTests/ExecutionStrategyTest.cs +++ b/test/EFCore.PG.FunctionalTests/ExecutionStrategyTest.cs @@ -99,7 +99,7 @@ private void Test_commit_failure(bool realFailure, Action l.Id == CoreEventId.ExecutionStrategyRetrying)); + Assert.DoesNotContain(Fixture.TestSqlLoggerFactory.Log, l => l.Id == CoreEventId.ExecutionStrategyRetrying); } Assert.Equal(realFailure ? 3 : 2, connection.OpenCount); @@ -213,7 +213,7 @@ private async Task Test_commit_failure_async( } else { - Assert.Empty(Fixture.TestSqlLoggerFactory.Log.Where(l => l.Id == CoreEventId.ExecutionStrategyRetrying)); + Assert.DoesNotContain(Fixture.TestSqlLoggerFactory.Log, l => l.Id == CoreEventId.ExecutionStrategyRetrying); } Assert.Equal(realFailure ? 3 : 2, connection.OpenCount); diff --git a/test/EFCore.PG.FunctionalTests/JsonTypesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/JsonTypesNpgsqlTest.cs index d5307dbbd..6806f8fa0 100644 --- a/test/EFCore.PG.FunctionalTests/JsonTypesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/JsonTypesNpgsqlTest.cs @@ -6,6 +6,7 @@ using System.Numerics; using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities; +using Xunit.Sdk; namespace Npgsql.EntityFrameworkCore.PostgreSQL; @@ -17,76 +18,76 @@ public class JsonTypesNpgsqlTest : JsonTypesRelationalTestBase // supported). public override Task Can_read_write_array_of_array_of_array_of_int_JSON_values() - => Assert.ThrowsAsync(() => base.Can_read_write_array_of_array_of_array_of_int_JSON_values()); + => Assert.ThrowsAsync(() => base.Can_read_write_array_of_array_of_array_of_int_JSON_values()); public override Task Can_read_write_array_of_list_of_array_of_IPAddress_JSON_values() - => Assert.ThrowsAsync(() => base.Can_read_write_array_of_list_of_array_of_IPAddress_JSON_values()); + => Assert.ThrowsAsync(() => base.Can_read_write_array_of_list_of_array_of_IPAddress_JSON_values()); public override Task Can_read_write_array_of_list_of_array_of_string_JSON_values() - => Assert.ThrowsAsync(() => base.Can_read_write_array_of_list_of_array_of_string_JSON_values()); + => Assert.ThrowsAsync(() => base.Can_read_write_array_of_list_of_array_of_string_JSON_values()); public override Task Can_read_write_array_of_list_of_binary_JSON_values(string expected) - => Assert.ThrowsAsync(() => base.Can_read_write_array_of_list_of_binary_JSON_values(expected)); + => Assert.ThrowsAsync(() => base.Can_read_write_array_of_list_of_binary_JSON_values(expected)); public override Task Can_read_write_array_of_list_of_GUID_JSON_values(string expected) - => Assert.ThrowsAsync(() => base.Can_read_write_array_of_list_of_GUID_JSON_values(expected)); + => Assert.ThrowsAsync(() => base.Can_read_write_array_of_list_of_GUID_JSON_values(expected)); public override Task Can_read_write_array_of_list_of_int_JSON_values() - => Assert.ThrowsAsync(() => base.Can_read_write_array_of_list_of_int_JSON_values()); + => Assert.ThrowsAsync(() => base.Can_read_write_array_of_list_of_int_JSON_values()); public override Task Can_read_write_array_of_list_of_IPAddress_JSON_values() - => Assert.ThrowsAsync(() => base.Can_read_write_array_of_list_of_IPAddress_JSON_values()); + => Assert.ThrowsAsync(() => base.Can_read_write_array_of_list_of_IPAddress_JSON_values()); public override Task Can_read_write_array_of_list_of_string_JSON_values() - => Assert.ThrowsAsync(() => base.Can_read_write_array_of_list_of_string_JSON_values()); + => Assert.ThrowsAsync(() => base.Can_read_write_array_of_list_of_string_JSON_values()); public override Task Can_read_write_array_of_list_of_ulong_JSON_values() - => Assert.ThrowsAsync(() => base.Can_read_write_array_of_list_of_ulong_JSON_values()); + => Assert.ThrowsAsync(() => base.Can_read_write_array_of_list_of_ulong_JSON_values()); public override Task Can_read_write_list_of_array_of_GUID_JSON_values(string expected) - => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_GUID_JSON_values(expected)); + => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_GUID_JSON_values(expected)); public override Task Can_read_write_list_of_array_of_int_JSON_values() - => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_int_JSON_values()); + => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_int_JSON_values()); public override Task Can_read_write_list_of_array_of_IPAddress_JSON_values() - => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_IPAddress_JSON_values()); + => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_IPAddress_JSON_values()); public override Task Can_read_write_list_of_array_of_list_of_array_of_binary_JSON_values(string expected) - => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_list_of_array_of_binary_JSON_values(expected)); + => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_list_of_array_of_binary_JSON_values(expected)); public override Task Can_read_write_list_of_array_of_list_of_IPAddress_JSON_values() - => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_list_of_IPAddress_JSON_values()); + => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_list_of_IPAddress_JSON_values()); public override Task Can_read_write_list_of_array_of_list_of_string_JSON_values() - => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_list_of_string_JSON_values()); + => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_list_of_string_JSON_values()); public override Task Can_read_write_list_of_array_of_list_of_ulong_JSON_values() - => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_list_of_ulong_JSON_values()); + => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_list_of_ulong_JSON_values()); public override Task Can_read_write_list_of_array_of_nullable_GUID_JSON_values(string expected) - => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_nullable_GUID_JSON_values(expected)); + => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_nullable_GUID_JSON_values(expected)); public override Task Can_read_write_list_of_array_of_nullable_int_JSON_values() - => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_nullable_int_JSON_values()); + => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_nullable_int_JSON_values()); public override Task Can_read_write_list_of_array_of_nullable_ulong_JSON_values() - => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_nullable_ulong_JSON_values()); + => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_nullable_ulong_JSON_values()); public override Task Can_read_write_list_of_array_of_string_JSON_values() - => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_string_JSON_values()); + => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_string_JSON_values()); public override Task Can_read_write_list_of_array_of_ulong_JSON_values() - => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_ulong_JSON_values()); + => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_ulong_JSON_values()); public override Task Can_read_write_list_of_list_of_list_of_int_JSON_values() - => Assert.ThrowsAsync(() => base.Can_read_write_list_of_list_of_list_of_int_JSON_values()); + => Assert.ThrowsAsync(() => base.Can_read_write_list_of_list_of_list_of_int_JSON_values()); #endregion Nested collections (unsupported) // IEnumerable property public override Task Can_read_write_list_of_array_of_binary_JSON_values(string expected) - => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_binary_JSON_values(expected)); + => Assert.ThrowsAsync(() => base.Can_read_write_list_of_array_of_binary_JSON_values(expected)); // public override Task Can_read_write_ulong_enum_JSON_values(EnumU64 value, string json) // { diff --git a/test/EFCore.PG.FunctionalTests/Migrations/MigrationsInfrastructureNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Migrations/MigrationsInfrastructureNpgsqlTest.cs index 996e3402b..ba84d9a0a 100644 --- a/test/EFCore.PG.FunctionalTests/Migrations/MigrationsInfrastructureNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Migrations/MigrationsInfrastructureNpgsqlTest.cs @@ -58,7 +58,8 @@ public async Task Empty_Migration_Creates_Database() { await using var context = new BloggingContext( Fixture.TestStore.AddProviderOptions( - new DbContextOptionsBuilder().EnableServiceProviderCaching(false)).Options); + new DbContextOptionsBuilder().EnableServiceProviderCaching(false)) + .ConfigureWarnings(e => e.Log(RelationalEventId.PendingModelChangesWarning)).Options); var creator = (NpgsqlDatabaseCreator)context.GetService(); creator.RetryTimeout = TimeSpan.FromMinutes(10); @@ -185,21 +186,14 @@ protected override ITestStoreFactory TestStoreFactory public override MigrationsContext CreateContext() { var options = AddOptions( - new DbContextOptionsBuilder() + TestStore.AddProviderOptions(new DbContextOptionsBuilder()) .UseNpgsql( TestStore.ConnectionString, b => b.ApplyConfiguration() - .CommandTimeout(NpgsqlTestStore.CommandTimeout) - .SetPostgresVersion(TestEnvironment.PostgresVersion) - .ReverseNullOrdering())) - .UseInternalServiceProvider(CreateServiceProvider()) + .SetPostgresVersion(TestEnvironment.PostgresVersion))) + .UseInternalServiceProvider(ServiceProvider) .Options; return new MigrationsContext(options); } - - private static IServiceProvider CreateServiceProvider() - => new ServiceCollection() - .AddEntityFrameworkNpgsql() - .BuildServiceProvider(); } } } diff --git a/test/EFCore.PG.FunctionalTests/Query/AdHocJsonQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/AdHocJsonQueryNpgsqlTest.cs index cec0ec78a..056aab9d1 100644 --- a/test/EFCore.PG.FunctionalTests/Query/AdHocJsonQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/AdHocJsonQueryNpgsqlTest.cs @@ -7,7 +7,7 @@ public class AdHocJsonQueryNpgsqlTest : AdHocJsonQueryTestBase protected override ITestStoreFactory TestStoreFactory => NpgsqlTestStoreFactory.Instance; - protected override async Task Seed29219(MyContext29219 ctx) + protected override async Task Seed29219(DbContext ctx) { var entity1 = new MyEntity29219 { @@ -28,7 +28,7 @@ protected override async Task Seed29219(MyContext29219 ctx) Collection = [new() { NonNullableScalar = 1001, NullableScalar = null }] }; - ctx.Entities.AddRange(entity1, entity2); + ctx.AddRange(entity1, entity2); await ctx.SaveChangesAsync(); await ctx.Database.ExecuteSqlAsync( @@ -38,7 +38,7 @@ await ctx.Database.ExecuteSqlAsync( """); } - protected override async Task Seed30028(MyContext30028 ctx) + protected override async Task Seed30028(DbContext ctx) { // complete await ctx.Database.ExecuteSqlAsync( @@ -77,14 +77,14 @@ await ctx.Database.ExecuteSqlAsync( """); } - protected override async Task Seed33046(Context33046 ctx) + protected override async Task Seed33046(DbContext ctx) => await ctx.Database.ExecuteSqlAsync( $$""" INSERT INTO "Reviews" ("Rounds", "Id") VALUES('[{"RoundNumber":11,"SubRounds":[{"SubRoundNumber":111},{"SubRoundNumber":112}]}]', 1) """); - protected override async Task SeedArrayOfPrimitives(MyContextArrayOfPrimitives ctx) + protected override async Task SeedArrayOfPrimitives(DbContext ctx) { var entity1 = new MyEntityArrayOfPrimitives { @@ -126,11 +126,11 @@ protected override async Task SeedArrayOfPrimitives(MyContextArrayOfPrimitives c ] }; - ctx.Entities.AddRange(entity1, entity2); + ctx.AddRange(entity1, entity2); await ctx.SaveChangesAsync(); } - protected override async Task SeedJunkInJson(MyContextJunkInJson ctx) + protected override async Task SeedJunkInJson(DbContext ctx) => await ctx.Database.ExecuteSqlAsync( $$$""" INSERT INTO "Entities" ("Collection", "CollectionWithCtor", "Reference", "ReferenceWithCtor", "Id") @@ -142,7 +142,7 @@ protected override async Task SeedJunkInJson(MyContextJunkInJson ctx) 1) """); - protected override async Task SeedTrickyBuffering(MyContextTrickyBuffering ctx) + protected override async Task SeedTrickyBuffering(DbContext ctx) => await ctx.Database.ExecuteSqlAsync( $$$""" INSERT INTO "Entities" ("Reference", "Id") @@ -150,7 +150,7 @@ protected override async Task SeedTrickyBuffering(MyContextTrickyBuffering ctx) '{"Name": "r1", "Number": 7, "JunkReference":{"Something": "SomeValue" }, "JunkCollection": [{"Foo": "junk value"}], "NestedReference": {"DoB": "2000-01-01T00:00:00Z"}, "NestedCollection": [{"DoB": "2000-02-01T00:00:00Z", "JunkReference": {"Something": "SomeValue"}}, {"DoB": "2000-02-02T00:00:00Z"}]}',1) """); - protected override async Task SeedShadowProperties(MyContextShadowProperties ctx) + protected override async Task SeedShadowProperties(DbContext ctx) => await ctx.Database.ExecuteSqlAsync( $$""" INSERT INTO "Entities" ("Collection", "CollectionWithCtor", "Reference", "ReferenceWithCtor", "Id", "Name") @@ -163,7 +163,7 @@ protected override async Task SeedShadowProperties(MyContextShadowProperties ctx 'e1') """); - protected override async Task SeedNotICollection(MyContextNotICollection ctx) + protected override async Task SeedNotICollection(DbContext ctx) { await ctx.Database.ExecuteSqlAsync( $$""" diff --git a/test/EFCore.PG.FunctionalTests/Query/JsonQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/JsonQueryNpgsqlTest.cs index 9af98af68..e4706b39b 100644 --- a/test/EFCore.PG.FunctionalTests/Query/JsonQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/JsonQueryNpgsqlTest.cs @@ -3,7 +3,7 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query; -public class JsonQueryNpgsqlTest : JsonQueryTestBase +public class JsonQueryNpgsqlTest : JsonQueryRelationalTestBase { public JsonQueryNpgsqlTest(JsonQueryNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) : base(fixture) @@ -3370,7 +3370,7 @@ public virtual void Check_all_tests_overridden() private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); - public class JsonQueryNpgsqlFixture : JsonQueryFixtureBase, IQueryFixtureBase + public class JsonQueryNpgsqlFixture : JsonQueryRelationalFixture, IQueryFixtureBase { protected override ITestStoreFactory TestStoreFactory => NpgsqlTestStoreFactory.Instance; diff --git a/test/EFCore.PG.FunctionalTests/Query/NonSharedPrimitiveCollectionsQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/NonSharedPrimitiveCollectionsQueryNpgsqlTest.cs index fa4e4ab1b..399c8cd84 100644 --- a/test/EFCore.PG.FunctionalTests/Query/NonSharedPrimitiveCollectionsQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/NonSharedPrimitiveCollectionsQueryNpgsqlTest.cs @@ -1,3 +1,4 @@ +using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; using Npgsql.EntityFrameworkCore.PostgreSQL.Internal; using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities; @@ -95,6 +96,20 @@ LIMIT 2 """); } + protected override DbContextOptionsBuilder SetTranslateParameterizedCollectionsToConstants(DbContextOptionsBuilder optionsBuilder) + { + new NpgsqlDbContextOptionsBuilder(optionsBuilder).TranslateParameterizedCollectionsToConstants(); + + return optionsBuilder; + } + + protected override DbContextOptionsBuilder SetTranslateParameterizedCollectionsToParameters(DbContextOptionsBuilder optionsBuilder) + { + new NpgsqlDbContextOptionsBuilder(optionsBuilder).TranslateParameterizedCollectionsToParameters(); + + return optionsBuilder; + } + protected override ITestStoreFactory TestStoreFactory => NpgsqlTestStoreFactory.Instance; } diff --git a/test/EFCore.PG.FunctionalTests/Query/NorthwindAggregateOperatorsQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/NorthwindAggregateOperatorsQueryNpgsqlTest.cs index e5dc276ad..6765d9481 100644 --- a/test/EFCore.PG.FunctionalTests/Query/NorthwindAggregateOperatorsQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/NorthwindAggregateOperatorsQueryNpgsqlTest.cs @@ -16,14 +16,33 @@ public NorthwindAggregateOperatorsQueryNpgsqlTest( } // Overriding to add equality tolerance because of floating point precision - [ConditionalTheory] - [MemberData(nameof(IsAsyncData))] - public override Task Average_over_max_subquery_is_client_eval(bool async) - => AssertAverage( + public override async Task Average_over_max_subquery(bool async) + { + await AssertAverage( async, ss => ss.Set().OrderBy(c => c.CustomerID).Take(3), selector: c => (decimal)c.Orders.Average(o => 5 + o.OrderDetails.Max(od => od.ProductID)), - asserter: (a, b) => Assert.Equal(a, b, 15)); + asserter: (e, a) => Assert.Equal(e, a, 10)); + + AssertSql( + """ +@__p_0='3' + +SELECT avg(( + SELECT avg(CAST(5 + ( + SELECT max(o0."ProductID") + FROM "Order Details" AS o0 + WHERE o."OrderID" = o0."OrderID") AS double precision)) + FROM "Orders" AS o + WHERE c0."CustomerID" = o."CustomerID")::numeric) +FROM ( + SELECT c."CustomerID" + FROM "Customers" AS c + ORDER BY c."CustomerID" NULLS FIRST + LIMIT @__p_0 +) AS c0 +"""); + } public override async Task Contains_with_local_uint_array_closure(bool async) { diff --git a/test/EFCore.PG.FunctionalTests/Query/PrimitiveCollectionsQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/PrimitiveCollectionsQueryNpgsqlTest.cs index 9b8cd9926..7802ed3e7 100644 --- a/test/EFCore.PG.FunctionalTests/Query/PrimitiveCollectionsQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/PrimitiveCollectionsQueryNpgsqlTest.cs @@ -148,18 +148,6 @@ public override async Task Inline_collection_Contains_with_three_values(bool asy """); } - public override async Task Inline_collection_Contains_with_EF_Constant(bool async) - { - await base.Inline_collection_Contains_with_EF_Constant(async); - - AssertSql( - """ -SELECT p."Id", p."Bool", p."Bools", p."DateTime", p."DateTimes", p."Enum", p."Enums", p."Int", p."Ints", p."NullableInt", p."NullableInts", p."NullableString", p."NullableStrings", p."String", p."Strings" -FROM "PrimitiveCollectionsEntity" AS p -WHERE p."Id" IN (2, 999, 1000) -"""); - } - public override async Task Inline_collection_Contains_with_all_parameters(bool async) { await base.Inline_collection_Contains_with_all_parameters(async); @@ -397,6 +385,68 @@ WHERE GREATEST(30, p."NullableInt", NULL) = 30 """); } + public override async Task Inline_collection_with_single_parameter_element_Contains(bool async) + { + await base.Inline_collection_with_single_parameter_element_Contains(async); + + AssertSql( + """ +@__i_0='2' + +SELECT p."Id", p."Bool", p."Bools", p."DateTime", p."DateTimes", p."Enum", p."Enums", p."Int", p."Ints", p."NullableInt", p."NullableInts", p."NullableString", p."NullableStrings", p."String", p."Strings" +FROM "PrimitiveCollectionsEntity" AS p +WHERE p."Id" = @__i_0 +"""); + } + + public override async Task Inline_collection_with_single_parameter_element_Count(bool async) + { + await base.Inline_collection_with_single_parameter_element_Count(async); + + AssertSql( + """ +@__i_0='2' + +SELECT p."Id", p."Bool", p."Bools", p."DateTime", p."DateTimes", p."Enum", p."Enums", p."Int", p."Ints", p."NullableInt", p."NullableInts", p."NullableString", p."NullableStrings", p."String", p."Strings" +FROM "PrimitiveCollectionsEntity" AS p +WHERE ( + SELECT count(*)::int + FROM (VALUES (@__i_0::int)) AS v("Value") + WHERE v."Value" > p."Id") = 1 +"""); + } + + public override async Task Inline_collection_Contains_with_EF_Parameter(bool async) + { + await base.Inline_collection_Contains_with_EF_Parameter(async); + + AssertSql( + """ +@__p_0={ '2', '999', '1000' } (DbType = Object) + +SELECT p."Id", p."Bool", p."Bools", p."DateTime", p."DateTimes", p."Enum", p."Enums", p."Int", p."Ints", p."NullableInt", p."NullableInts", p."NullableString", p."NullableStrings", p."String", p."Strings" +FROM "PrimitiveCollectionsEntity" AS p +WHERE p."Id" = ANY (@__p_0) +"""); + } + + public override async Task Inline_collection_Count_with_column_predicate_with_EF_Parameter(bool async) + { + await base.Inline_collection_Count_with_column_predicate_with_EF_Parameter(async); + + AssertSql( + """ +@__p_0={ '2', '999', '1000' } (DbType = Object) + +SELECT p."Id", p."Bool", p."Bools", p."DateTime", p."DateTimes", p."Enum", p."Enums", p."Int", p."Ints", p."NullableInt", p."NullableInts", p."NullableString", p."NullableStrings", p."String", p."Strings" +FROM "PrimitiveCollectionsEntity" AS p +WHERE ( + SELECT count(*)::int + FROM unnest(@__p_0) AS p0(value) + WHERE p0.value > p."Id") = 2 +"""); + } + public override async Task Parameter_collection_Count(bool async) { await base.Parameter_collection_Count(async); @@ -666,6 +716,48 @@ public override async Task Parameter_collection_null_Contains(bool async) """); } + public override async Task Parameter_collection_Contains_with_EF_Constant(bool async) + { + await base.Parameter_collection_Contains_with_EF_Constant(async); + + AssertSql( + """ +SELECT p."Id", p."Bool", p."Bools", p."DateTime", p."DateTimes", p."Enum", p."Enums", p."Int", p."Ints", p."NullableInt", p."NullableInts", p."NullableString", p."NullableStrings", p."String", p."Strings" +FROM "PrimitiveCollectionsEntity" AS p +WHERE p."Id" IN (2, 999, 1000) +"""); + } + + public override async Task Parameter_collection_Where_with_EF_Constant_Where_Any(bool async) + { + await base.Parameter_collection_Where_with_EF_Constant_Where_Any(async); + + AssertSql( + """ +SELECT p."Id", p."Bool", p."Bools", p."DateTime", p."DateTimes", p."Enum", p."Enums", p."Int", p."Ints", p."NullableInt", p."NullableInts", p."NullableString", p."NullableStrings", p."String", p."Strings" +FROM "PrimitiveCollectionsEntity" AS p +WHERE EXISTS ( + SELECT 1 + FROM (VALUES (2), (999), (1000)) AS i("Value") + WHERE i."Value" > 0) +"""); + } + + public override async Task Parameter_collection_Count_with_column_predicate_with_EF_Constant(bool async) + { + await base.Parameter_collection_Count_with_column_predicate_with_EF_Constant(async); + + AssertSql( + """ +SELECT p."Id", p."Bool", p."Bools", p."DateTime", p."DateTimes", p."Enum", p."Enums", p."Int", p."Ints", p."NullableInt", p."NullableInts", p."NullableString", p."NullableStrings", p."String", p."Strings" +FROM "PrimitiveCollectionsEntity" AS p +WHERE ( + SELECT count(*)::int + FROM (VALUES (2), (999), (1000)) AS i("Value") + WHERE i."Value" > p."Id") = 2 +"""); + } + [ConditionalTheory] // #3012 [MinimumPostgresVersion(14, 0)] // Multiranges were introduced in PostgreSQL 14 [MemberData(nameof(IsAsyncData))] diff --git a/test/EFCore.PG.FunctionalTests/Scaffolding/NpgsqlDatabaseModelFactoryTest.cs b/test/EFCore.PG.FunctionalTests/Scaffolding/NpgsqlDatabaseModelFactoryTest.cs index c26bcc273..5696d082b 100644 --- a/test/EFCore.PG.FunctionalTests/Scaffolding/NpgsqlDatabaseModelFactoryTest.cs +++ b/test/EFCore.PG.FunctionalTests/Scaffolding/NpgsqlDatabaseModelFactoryTest.cs @@ -396,11 +396,11 @@ FOREIGN KEY ("ForeignKeyId1", "ForeignKeyId2") REFERENCES "db2"."PrincipalTable" Assert.Equal("db2", sequence.Schema); Assert.Single(dbModel.Tables.Where(t => t.Schema == "db.2" && t.Name == "QuotedTableName")); - Assert.Empty(dbModel.Tables.Where(t => t.Schema == "db.2" && t.Name == "Table.With.Dot")); + Assert.DoesNotContain(dbModel.Tables, t => t.Schema == "db.2" && t.Name == "Table.With.Dot"); Assert.Single(dbModel.Tables.Where(t => t.Schema == "db.2" && t.Name == "SimpleTableName")); Assert.Single(dbModel.Tables.Where(t => t.Schema == "db.2" && t.Name == "JustTableName")); - Assert.Empty(dbModel.Tables.Where(t => t.Schema == "public" && t.Name == "QuotedTableName")); + Assert.DoesNotContain(dbModel.Tables, t => t.Schema == "public" && t.Name == "QuotedTableName"); Assert.Single(dbModel.Tables.Where(t => t.Schema == "public" && t.Name == "Table.With.Dot")); Assert.Single(dbModel.Tables.Where(t => t.Schema == "public" && t.Name == "SimpleTableName")); Assert.Single(dbModel.Tables.Where(t => t.Schema == "public" && t.Name == "JustTableName")); diff --git a/test/EFCore.PG.FunctionalTests/Update/JsonUpdateNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Update/JsonUpdateNpgsqlTest.cs index 20dc78487..4db135758 100644 --- a/test/EFCore.PG.FunctionalTests/Update/JsonUpdateNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Update/JsonUpdateNpgsqlTest.cs @@ -1992,37 +1992,37 @@ public override Task Edit_single_property_relational_collection_of_nullable_enum #region Skipped tests because of nested collections outside of JSON (nested arrays not supported in PG) public override Task Edit_single_property_collection_of_collection_of_bool() - => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); + => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); public override Task Edit_single_property_collection_of_collection_of_char() - => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); + => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); public override Task Edit_single_property_collection_of_collection_of_double() - => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); + => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); public override Task Edit_single_property_collection_of_collection_of_int16() - => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); + => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); public override Task Edit_single_property_collection_of_collection_of_int32() - => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); + => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); public override Task Edit_single_property_collection_of_collection_of_int64() - => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); + => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); public override Task Edit_single_property_collection_of_collection_of_single() - => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); + => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); public override Task Edit_single_property_collection_of_collection_of_nullable_int32() - => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); + => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); public override Task Edit_single_property_collection_of_collection_of_nullable_int32_set_to_null() - => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); + => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); public override Task Edit_single_property_collection_of_collection_of_nullable_enum_set_to_null() - => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); + => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); public override Task Edit_single_property_collection_of_collection_of_nullable_enum_with_int_converter() - => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); + => Assert.ThrowsAsync(() => base.Edit_single_property_collection_of_collection_of_bool()); #endregion Skipped tests because of nested collections outside of JSON (nested arrays not supported in PG)