diff --git a/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs b/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs index 782dccc1e..ea3850163 100644 --- a/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs +++ b/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs @@ -918,6 +918,32 @@ protected override void Generate( builder.EndCommand(); } + protected override void Generate(CreateSequenceOperation operation, IModel model, MigrationCommandListBuilder builder) + { + Check.NotNull(operation, nameof(operation)); + Check.NotNull(builder, nameof(builder)); + + if (VersionAtLeast(10, 0)) + { + // "CREATE SEQUENCE name AS type" expression is supported only in PostgreSQL 10 or above. + // The base MigrationsSqlGenerator.Generate method generates that expression. + // https://github.com/aspnet/EntityFrameworkCore/blob/master/src/EFCore.Relational/Migrations/MigrationsSqlGenerator.cs#L533-L535 + base.Generate(operation, model, builder); + return; + } + + // Generate CREATE SEQUENCE without "AS" part. + builder + .Append("CREATE SEQUENCE ") + .Append(_sqlGenerationHelper.DelimitIdentifier(operation.Name, operation.Schema)) + .Append(" START WITH ") + .Append(_typeMappingSource.GetMapping(typeof(long)).GenerateSqlLiteral(operation.StartValue)); + + SequenceOptions(operation, model, builder); + builder.AppendLine(_sqlGenerationHelper.StatementTerminator); + EndStatement(builder); + } + #endregion Standard migrations #region Utilities diff --git a/test/EFCore.PG.FunctionalTests/NpgsqlMigrationSqlGeneratorTest.cs b/test/EFCore.PG.FunctionalTests/NpgsqlMigrationSqlGeneratorTest.cs index fa53558ef..f79c9ebae 100644 --- a/test/EFCore.PG.FunctionalTests/NpgsqlMigrationSqlGeneratorTest.cs +++ b/test/EFCore.PG.FunctionalTests/NpgsqlMigrationSqlGeneratorTest.cs @@ -209,6 +209,7 @@ public virtual void CreateDatabaseOperation_with_tablespace() Sql); } + [Fact] public override void CreateSequenceOperation_with_minValue_and_maxValue() { base.CreateSequenceOperation_with_minValue_and_maxValue(); @@ -218,12 +219,24 @@ public override void CreateSequenceOperation_with_minValue_and_maxValue() Sql); } + [Fact] public override void CreateSequenceOperation_with_minValue_and_maxValue_not_long() { - // In PostgreSQL, sequence data types are always bigint. - // http://www.postgresql.org/docs/9.4/static/infoschema-sequences.html + base.CreateSequenceOperation_with_minValue_and_maxValue_not_long(); + Assert.Equal( + "CREATE SEQUENCE dbo.\"EntityFrameworkHiLoSequence\" AS integer START WITH 3 INCREMENT BY 1 MINVALUE 2 MAXVALUE 816 CYCLE;" + EOL, + Sql); + + using (TestHelpers.WithPostgresVersion(new Version(9, 5))) + { + base.CreateSequenceOperation_with_minValue_and_maxValue_not_long(); + Assert.Equal( + "CREATE SEQUENCE dbo.\"EntityFrameworkHiLoSequence\" START WITH 3 INCREMENT BY 1 MINVALUE 2 MAXVALUE 816 CYCLE;" + EOL, + Sql); + } } + [Fact] public override void CreateSequenceOperation_without_minValue_and_maxValue() { base.CreateSequenceOperation_without_minValue_and_maxValue(); @@ -1050,6 +1063,20 @@ public void CreateSequenceOperation_with_data_type_smallint() Assert.StartsWith( "CREATE SEQUENCE public.short_sequence AS smallint", Sql); + + using(TestHelpers.WithPostgresVersion(new Version(9, 5))) + { + Generate( + new CreateSequenceOperation { + Name = "short_sequence", + Schema = "public", + ClrType = typeof(short) + }); + + Assert.StartsWith( + "CREATE SEQUENCE public.short_sequence", + Sql); + } } #endregion Sequence data types @@ -1154,6 +1181,8 @@ public void FixedLength() ");" + EOL, Sql); } + protected new NpgsqlTestHelpers TestHelpers => (NpgsqlTestHelpers)base.TestHelpers; + public NpgsqlMigrationSqlGeneratorTest() : base(NpgsqlTestHelpers.Instance) { diff --git a/test/EFCore.PG.FunctionalTests/TestUtilities/NpgsqlTestHelpers.cs b/test/EFCore.PG.FunctionalTests/TestUtilities/NpgsqlTestHelpers.cs index 4f6eb02f7..242321cb4 100644 --- a/test/EFCore.PG.FunctionalTests/TestUtilities/NpgsqlTestHelpers.cs +++ b/test/EFCore.PG.FunctionalTests/TestUtilities/NpgsqlTestHelpers.cs @@ -1,21 +1,45 @@ -using Microsoft.EntityFrameworkCore; +using System; +using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.TestUtilities; using Microsoft.Extensions.DependencyInjection; +using Xunit; namespace Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities { public class NpgsqlTestHelpers : TestHelpers { - protected NpgsqlTestHelpers() + Version _postgresVersion; + + public class VersionScope : IDisposable { + NpgsqlTestHelpers _helpers; + Version _oldVersion, _newVersion; + + internal VersionScope(NpgsqlTestHelpers helpers, Version version) + { + _helpers = helpers; + _oldVersion = helpers._postgresVersion; + _newVersion = helpers._postgresVersion = version; + } + + public void Dispose() + { + Assert.Equal(_helpers._postgresVersion, _newVersion); + _helpers._postgresVersion = _oldVersion; + } } + public VersionScope WithPostgresVersion(Version version) => new VersionScope(this, version); + + protected NpgsqlTestHelpers() {} + public static NpgsqlTestHelpers Instance { get; } = new NpgsqlTestHelpers(); public override IServiceCollection AddProviderServices(IServiceCollection services) => services.AddEntityFrameworkNpgsql(); protected override void UseProviderOptions(DbContextOptionsBuilder optionsBuilder) - => optionsBuilder.UseNpgsql(new NpgsqlConnection("Database=DummyDatabase")); + => optionsBuilder.UseNpgsql(new NpgsqlConnection("Database=DummyDatabase"), + options => options.SetPostgresVersion(_postgresVersion)); } }