-
Notifications
You must be signed in to change notification settings - Fork 255
Fix migration bug with PostgreSQL 9.5 #607
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
@slavanap Thanks for opening this PR. We've been working on various version-compatibility features for the next release, so it's nice to see that users are thinking about version-compatibility too. Could you give us some additional details describing what you're seeing now (including version info for PostgreSQL/Npgsql/EF Core), and what you expect to see after this PR? Related |
|
@austindrenski Thanks for the fast reply. Without changes from this PR I faced issue during migrations history application when upgrade from netcore2.0 to netcore2.1. With this patch migrations work with no issue, if target database is created beforehand. (I don't know if you can fix Exception on database creation with Npgsql as well.) I do apply migrations automatically with this code in my |
|
The tests failed because they expect "AS type" part in CREATE SEQUENCE statement. I'm not sure there is some variable that specifies PostgreSQL backend version, so I could split each of related tests into 2 for my change. Moreover, it seems tests are run only for PostgreSQL version 10 at Appveyor & Travis. |
|
This was introduced in #301. The problem here is that this "fix" also reverts the support for the new, non-bigint sequences, and I don't think we want that. In my opinion, the current behavior should remain the default - the provider assumes the latest version of PostgreSQL is used, and supports different sequence data types with AS. However, we should give the user a compat option, in which case the provider reverts to the previous behavior (always The problem is that the backwards compat feature, #483, wasn't backported to 2.1. @austindrenski, what do you think? Is it feasible (and risk-free) to backport this to 2.1 and add this conditional behavior? |
|
@roji, I agree on some points. I suggest to treat at least sequences of |
I understand, but if you handle int sequences as bigint, you're effectively denying PostgreSQL 10 users the option of defining integer sequences in the database... I admit I'm not sure about this - it's true that lots of people out there are using int sequences in C#, and their code doesn't work if they try to run on PostgreSQL 9. But I'm thinking that a compatibility option as suggested above, plus a clear note in the 2.1 "breaking changes" docs should be enough... |
May it request server version instead of the assumption? If similar changes would be made in PostgreSQL 11, similar issues will appear. AFAIK, it's still PostgreSQL 9 installed by default on Ubuntu Xenial and Debian Stretch. Not sure about Ubuntu Bionic. |
Unfortunately not, the service responsible for generating migration SQL ( I do understand the pain of having the latest EF Core throw on the default PostgreSQL with standard code. Let me think about this for a bit (and let's see what @austindrenski thinks as well). |
|
@roji wrote:
Agreed. @roji wrote:
I think #483 is sufficiently safe to backport (specifically, 04ce9cd). I'll also put together a PR to inject it into @roji wrote:
Agreed.
We're certainly sympathetic to defaults in the wild... but EF Core was designed to be opinionated and forward-leaning. This is a great discussion on compatibility, and I'm happy that we're having it. But I do think that we can address your concerns without impacting support for features from the latest release of PostgreSQL. |
|
@slavanap are you interested in continuing work on this PR, once we merge the backwards compatibility support? |
|
I'm glad you guys paid attention on the issue already. I've read comments in #612 and rewrote mine PR on top of it. Please, review if there's any additional changes necessary. Unfortunately, I couldn't figure out fast how to pass NpgsqlOptionsExtension.PostgresVersion in tests, just tested locally that it fixed my original issue with the option set @roji, Do you mean merge these changes on top of hotfix/2.1.2 branch? |
| // "CREATE SEQUENCE name AS type" expression is supported only in PostgreSQL 10 or above | ||
| // There is slightly modified version of MigrationsSqlGenerator.Generate below. The reason is here: | ||
| // https://github.com/aspnet/EntityFrameworkCore/blob/master/src/EFCore.Relational/Migrations/MigrationsSqlGenerator.cs#L533-L535 | ||
| Check.NotNull(operation, nameof(operation)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These guards should be moved to the top of the method.
|
|
||
| var typeMapping = Dependencies.TypeMappingSource.GetMapping(operation.ClrType); | ||
| if (operation.ClrType != typeof(long)) | ||
| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: omit the braces on a one-liner if
| if (operation.ClrType != typeof(long)) | ||
| { | ||
| // set the typeMapping for use with operation.StartValue (i.e. a long) below | ||
| typeMapping = Dependencies.TypeMappingSource.GetMapping(typeof(long)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_typeMappingSource is available as an instance field. Use that instead.
|
I've merged #612. Please rebase when you address the review. |
|
|
||
| builder | ||
| .Append("CREATE SEQUENCE ") | ||
| .Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name, operation.Schema)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should also use the instance field for _sqlGenerationHelper.
| .Append("CREATE SEQUENCE ") | ||
| .Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name, operation.Schema)); | ||
|
|
||
| var typeMapping = Dependencies.TypeMappingSource.GetMapping(operation.ClrType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should also use the instance field for _typeMappingSource.
|
|
||
| builder | ||
| .Append(" START WITH ") | ||
| .Append(typeMapping.GenerateSqlLiteral(operation.StartValue)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here too
| .Append(typeMapping.GenerateSqlLiteral(operation.StartValue)); | ||
|
|
||
| SequenceOptions(operation, model, builder); | ||
| builder.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here too
|
Applied requested changes and simplified the code. Now I wonder why have tests started to flicker. |
austindrenski
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking good. One last style comment, then we need some tests added to the functional test suite.
| builder.EndCommand(); | ||
| } | ||
|
|
||
| protected override void Generate(CreateSequenceOperation operation, IModel model, MigrationCommandListBuilder builder) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move this brace to the following line
|
@slavanap Just an intermittently flaky test. I re-built the PR and it passed. Please take a look at my review, and let us know if you need some advice on how to go about adding functional tests. |
|
One difficulty in writing tests for me now is how to set I'll continue tomorrow. |
|
For some examples, take a look at the tests I'm working on in #541: You'll likely need to refactor some of the code in the support regions like here: The tests in #541 are somewhat incomplete, as they only test that we perform the translation given a supported version number. For this PR, you will need to test the translations on both sides of version |
|
@austindrenski I've found tests that are related to the subject and modified them. Now I'm thinking how to apply "Fixture" pattern to them as well. Well, if that's not possible, should I remove those tests completely and replace them with tests in a new file? What do you think? |
|
In my opinion, these tests should ideally live in NpgsqlMigrationSqlGeneratorTest, although it might be tricky to change the context for the compatibility options... Let's give it a try though. |
|
@roji have you seen my modifications of NpgsqlMigrationSqlGeneratorTest in this PR? |
austindrenski
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's see what @roji thinks, but I think this is looking good.
In particular, I like your VersionScope class. It's a clever way of solving the "overriding tests is complicated"-problem by explicitly scoping your configuration changes.
I've left a few other nits addressing what look like some C++/C# style differences.
Overall, I think this is very close to mergeable.
| namespace Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities | ||
| { | ||
| public class NpgsqlTestHelpers : TestHelpers | ||
| public class NpgsqlTestHelpers: TestHelpers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: revert
| public class NpgsqlTestHelpers : TestHelpers | ||
| public class NpgsqlTestHelpers: TestHelpers | ||
| { | ||
| private System.Version PostgresVersion; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Import the System namespace at the top.
nit: Rename to _postgresVersion.
nit: Omit the (implicit) private modifier
| { | ||
| private System.Version PostgresVersion; | ||
|
|
||
| public class VersionScope : System.IDisposable { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: As above, drop the qualifier.
nit: Bring this brace down to the next line.
| public class VersionScope : System.IDisposable { | ||
| private NpgsqlTestHelpers _helpers; | ||
| private System.Version _oldVersion, _newVersion; | ||
| public VersionScope(NpgsqlTestHelpers helpers, System.Version version) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Add a line break between the fields and constructor.
nit: Bring this brace down to the next line.
| _oldVersion = helpers.PostgresVersion; | ||
| _newVersion = helpers.PostgresVersion = version; | ||
| } | ||
| public void Dispose() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Bring this brace down to the next line.
| } | ||
| } | ||
|
|
||
| protected NpgsqlTestHelpers() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this was already here...but would you mind bringing these braces up like so (note the spacing)?
protected NpgsqlTestHelpers() {}| => optionsBuilder.UseNpgsql(new NpgsqlConnection("Database=DummyDatabase"), | ||
| options => options.SetPostgresVersion(PostgresVersion)); | ||
|
|
||
| public VersionScope WithPostgresVersion(System.Version version) => new VersionScope(this, version); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move this above the constructor.
| options => options.SetPostgresVersion(PostgresVersion)); | ||
|
|
||
| public VersionScope WithPostgresVersion(System.Version version) => new VersionScope(this, version); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Omit trailing line breaks.
|
Also extended CreateSequenceOperation_with_data_type_smallint test (dc9f75e#diff-be2409a5108c69d00b643dff9fd0df53R1054) I wish MS will implement code style settings per project one day. |
|
@slavanap Could you rebase on |
… function because of its incompatibility with PostgreSQL servers version below 10
|
@austindrenski Done. |
|
@slavanap Thanks for your contribution! |
|
@austindrenski Thanks for your review! It was valuable experience. |
| builder.EndCommand(); | ||
| } | ||
|
|
||
| protected override void Generate(CreateSequenceOperation operation, IModel model, MigrationCommandListBuilder builder) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it be better to simply call base.Generate() with a CreateSequenceOperation that has ClrType overridden to long? This would prevent the duplication of that code in this function...
(@austindrenski it's better to wait for me to review too, I know I'm a bit busy these days but still...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@roji Apologies on moving forward with the merge before your review. I'll open a follow-up PR to de-duplicate where possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@roji How would you suggest to clone CreateSequenceOperation operation class with all it's properties? Or you suggest modify its ClrType property "in-place" and return back to original value after calling base.Generate() method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changing in-place is a possibility, yes. I'm not even sure it's necessary to return back to the original value as these operations are only used for SQL generation - but for correctness's sake it's a good idea.
You could also copy properties one-by-one to a new instance, including annotations, but that would be tedious and brittle in case something gets added in the future.
Override MigrationsSqlGenerator.Generate(CreateSequenceOperation,...) function because of its incompatibility with PostgreSQL servers version below 10.