diff --git a/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs b/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs index b68799855..3e96d255f 100644 --- a/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs +++ b/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs @@ -405,7 +405,10 @@ protected override void Generate(AlterColumnOperation operation, IModel model, M .AppendLine($"ALTER SEQUENCE {sequence} RENAME TO {oldSequenceWithoutSchema};") .AppendLine($"{alterBase}DROP DEFAULT;") .AppendLine($"{alterBase}ADD GENERATED {identityTypeClause} AS IDENTITY;") - .AppendLine($"SELECT * FROM setval('{sequence}', nextval('{oldSequence}'), false);") + // When generating idempotent scripts, migration DDL is enclosed in anonymous DO blocks, + // where PERFORM must be used instead of SELECT + .Append(Options.HasFlag(MigrationsSqlGenerationOptions.Idempotent) ? "PERFORM" : "SELECT") + .AppendLine($" * FROM setval('{sequence}', nextval('{oldSequence}'), false);") .AppendLine($"DROP SEQUENCE {oldSequence};"); break; default: diff --git a/test/EFCore.PG.Tests/Migrations/NpgsqlMigrationsSqlGeneratorTest.cs b/test/EFCore.PG.Tests/Migrations/NpgsqlMigrationsSqlGeneratorTest.cs index aa56bf91a..daa08f842 100644 --- a/test/EFCore.PG.Tests/Migrations/NpgsqlMigrationsSqlGeneratorTest.cs +++ b/test/EFCore.PG.Tests/Migrations/NpgsqlMigrationsSqlGeneratorTest.cs @@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations.Operations; using Microsoft.Extensions.DependencyInjection; using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; @@ -425,6 +426,46 @@ public void CreateIndexOperation_collation() "); } + [Theory] + [InlineData(MigrationsSqlGenerationOptions.Default)] + [InlineData(MigrationsSqlGenerationOptions.Idempotent)] + public void Alter_column_change_serial_to_identity_idempotent(MigrationsSqlGenerationOptions options) + { + Generate( + modelBuilder => modelBuilder.Entity().Property("Id").UseSerialColumn(), + new[] + { + new AlterColumnOperation + { + Table = "Person", + Name = "Id", + ClrType = typeof(int), + [NpgsqlAnnotationNames.ValueGenerationStrategy] = + NpgsqlValueGenerationStrategy.IdentityByDefaultColumn, + + OldColumn = new AddColumnOperation + { + Table = "Person", + Name = "Id", + ClrType = typeof(int), + [NpgsqlAnnotationNames.ValueGenerationStrategy] = + NpgsqlValueGenerationStrategy.SerialColumn, + } + } + }, + options); + + AssertSql( + $@"ALTER TABLE ""Person"" ALTER COLUMN ""Id"" TYPE integer; +ALTER TABLE ""Person"" ALTER COLUMN ""Id"" SET NOT NULL; +ALTER SEQUENCE ""Person_Id_seq"" RENAME TO ""Person_Id_old_seq""; +ALTER TABLE ""Person"" ALTER COLUMN ""Id"" DROP DEFAULT; +ALTER TABLE ""Person"" ALTER COLUMN ""Id"" ADD GENERATED BY DEFAULT AS IDENTITY; +{(options == MigrationsSqlGenerationOptions.Idempotent ? "PERFORM" : "SELECT")} * FROM setval('""Person_Id_seq""', nextval('""Person_Id_old_seq""'), false); +DROP SEQUENCE ""Person_Id_old_seq""; +"); + } + #region CockroachDB interleave-in-parent // Note that we don't run tests against actual CockroachDB instances, so these are unit tests asserting on SQL