diff --git a/src/EFCore.MySql/Migrations/MySqlMigrationsSqlGenerator.cs b/src/EFCore.MySql/Migrations/MySqlMigrationsSqlGenerator.cs index 2ad1570a8..b048f9082 100644 --- a/src/EFCore.MySql/Migrations/MySqlMigrationsSqlGenerator.cs +++ b/src/EFCore.MySql/Migrations/MySqlMigrationsSqlGenerator.cs @@ -1191,7 +1191,8 @@ protected override void ColumnDefinition( if (operation.ComputedColumnSql == null) { - ColumnDefinitionWithCharSet(schema, table, name, operation, model, builder); + // AUTO_INCREMENT columns don't support DEFAULT values. + ColumnDefinitionWithCharSet(schema, table, name, operation, model, builder, withDefaultValue: !autoIncrement); GenerateComment(operation.Comment, builder); @@ -1305,7 +1306,14 @@ private void GenerateComment(string comment, MigrationCommandListBuilder builder .Append(MySqlStringTypeMapping.EscapeSqlLiteralWithLineBreaks(comment, !_options.NoBackslashEscapes, false)); } - private void ColumnDefinitionWithCharSet(string schema, string table, string name, ColumnOperation operation, IModel model, MigrationCommandListBuilder builder) + private void ColumnDefinitionWithCharSet( + string schema, + string table, + string name, + ColumnOperation operation, + IModel model, + MigrationCommandListBuilder builder, + bool withDefaultValue) { if (operation.ComputedColumnSql != null) { @@ -1322,7 +1330,10 @@ private void ColumnDefinitionWithCharSet(string schema, string table, string nam builder.Append(operation.IsNullable ? " NULL" : " NOT NULL"); - DefaultValue(operation.DefaultValue, operation.DefaultValueSql, columnType, builder); + if (withDefaultValue) + { + DefaultValue(operation.DefaultValue, operation.DefaultValueSql, columnType, builder); + } var srid = operation[MySqlAnnotationNames.SpatialReferenceSystemId]; if (srid is int && diff --git a/test/EFCore.MySql.FunctionalTests/FullMigrationsMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/FullMigrationsMySqlTest.cs index 977dae823..a6a173d8e 100644 --- a/test/EFCore.MySql.FunctionalTests/FullMigrationsMySqlTest.cs +++ b/test/EFCore.MySql.FunctionalTests/FullMigrationsMySqlTest.cs @@ -3,6 +3,7 @@ using System.Text.RegularExpressions; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.TestUtilities; using Microsoft.Extensions.DependencyInjection; @@ -362,6 +363,133 @@ IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000 DROP PROCEDURE `POMELO_AFTER_ADD_PRIMARY_KEY`; +""", + Sql, + ignoreLineEndingDifferences: true); + } + + [ConditionalFact] + public virtual void Alter_column_change_primary_key_will_not_try_to_declare_default_value_in_sql() + { + using var db = Fixture.CreateContext( + new ServiceCollection() + .AddScoped()); + + db.Database.EnsureDeleted(); + db.Database.EnsureCreated(); + + var migrator = (MySqlTestMigrator)db.GetService(); + migrator.MigrationsSqlGenerationOptionsOverrider = options => options & ~MigrationsSqlGenerationOptions.Script; + + SetSql(migrator.GenerateScript()); + + Assert.False(Sql.Contains("DEFAULT 0")); + Assert.Equal( +""" +CREATE TABLE IF NOT EXISTS `__EFMigrationsHistory` ( + `MigrationId` varchar(150) CHARACTER SET utf8mb4 NOT NULL, + `ProductVersion` varchar(32) CHARACTER SET utf8mb4 NOT NULL, + CONSTRAINT `PK___EFMigrationsHistory` PRIMARY KEY (`MigrationId`) +) CHARACTER SET=utf8mb4; + +START TRANSACTION; +CREATE TABLE `IceCreams` ( + `Name` varchar(32) NOT NULL, + CONSTRAINT `PK_IceCreams` PRIMARY KEY (`Name`) +); + +INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`) +VALUES ('00000000000001_Migration1', '7.0.0-test'); + +DROP PROCEDURE IF EXISTS `POMELO_BEFORE_DROP_PRIMARY_KEY`; +CREATE PROCEDURE `POMELO_BEFORE_DROP_PRIMARY_KEY`(IN `SCHEMA_NAME_ARGUMENT` VARCHAR(255), IN `TABLE_NAME_ARGUMENT` VARCHAR(255)) +BEGIN + DECLARE HAS_AUTO_INCREMENT_ID TINYINT(1); + DECLARE PRIMARY_KEY_COLUMN_NAME VARCHAR(255); + DECLARE PRIMARY_KEY_TYPE VARCHAR(255); + DECLARE SQL_EXP VARCHAR(1000); + SELECT COUNT(*) + INTO HAS_AUTO_INCREMENT_ID + FROM `information_schema`.`COLUMNS` + WHERE `TABLE_SCHEMA` = (SELECT IFNULL(SCHEMA_NAME_ARGUMENT, SCHEMA())) + AND `TABLE_NAME` = TABLE_NAME_ARGUMENT + AND `Extra` = 'auto_increment' + AND `COLUMN_KEY` = 'PRI' + LIMIT 1; + IF HAS_AUTO_INCREMENT_ID THEN + SELECT `COLUMN_TYPE` + INTO PRIMARY_KEY_TYPE + FROM `information_schema`.`COLUMNS` + WHERE `TABLE_SCHEMA` = (SELECT IFNULL(SCHEMA_NAME_ARGUMENT, SCHEMA())) + AND `TABLE_NAME` = TABLE_NAME_ARGUMENT + AND `COLUMN_KEY` = 'PRI' + LIMIT 1; + SELECT `COLUMN_NAME` + INTO PRIMARY_KEY_COLUMN_NAME + FROM `information_schema`.`COLUMNS` + WHERE `TABLE_SCHEMA` = (SELECT IFNULL(SCHEMA_NAME_ARGUMENT, SCHEMA())) + AND `TABLE_NAME` = TABLE_NAME_ARGUMENT + AND `COLUMN_KEY` = 'PRI' + LIMIT 1; + SET SQL_EXP = CONCAT('ALTER TABLE `', (SELECT IFNULL(SCHEMA_NAME_ARGUMENT, SCHEMA())), '`.`', TABLE_NAME_ARGUMENT, '` MODIFY COLUMN `', PRIMARY_KEY_COLUMN_NAME, '` ', PRIMARY_KEY_TYPE, ' NOT NULL;'); + SET @SQL_EXP = SQL_EXP; + PREPARE SQL_EXP_EXECUTE FROM @SQL_EXP; + EXECUTE SQL_EXP_EXECUTE; + DEALLOCATE PREPARE SQL_EXP_EXECUTE; + END IF; +END; +DROP PROCEDURE IF EXISTS `POMELO_AFTER_ADD_PRIMARY_KEY`; +CREATE PROCEDURE `POMELO_AFTER_ADD_PRIMARY_KEY`(IN `SCHEMA_NAME_ARGUMENT` VARCHAR(255), IN `TABLE_NAME_ARGUMENT` VARCHAR(255), IN `COLUMN_NAME_ARGUMENT` VARCHAR(255)) +BEGIN + DECLARE HAS_AUTO_INCREMENT_ID INT(11); + DECLARE PRIMARY_KEY_COLUMN_NAME VARCHAR(255); + DECLARE PRIMARY_KEY_TYPE VARCHAR(255); + DECLARE SQL_EXP VARCHAR(1000); + SELECT COUNT(*) + INTO HAS_AUTO_INCREMENT_ID + FROM `information_schema`.`COLUMNS` + WHERE `TABLE_SCHEMA` = (SELECT IFNULL(SCHEMA_NAME_ARGUMENT, SCHEMA())) + AND `TABLE_NAME` = TABLE_NAME_ARGUMENT + AND `COLUMN_NAME` = COLUMN_NAME_ARGUMENT + AND `COLUMN_TYPE` LIKE '%int%' + AND `COLUMN_KEY` = 'PRI'; + IF HAS_AUTO_INCREMENT_ID THEN + SELECT `COLUMN_TYPE` + INTO PRIMARY_KEY_TYPE + FROM `information_schema`.`COLUMNS` + WHERE `TABLE_SCHEMA` = (SELECT IFNULL(SCHEMA_NAME_ARGUMENT, SCHEMA())) + AND `TABLE_NAME` = TABLE_NAME_ARGUMENT + AND `COLUMN_NAME` = COLUMN_NAME_ARGUMENT + AND `COLUMN_TYPE` LIKE '%int%' + AND `COLUMN_KEY` = 'PRI'; + SELECT `COLUMN_NAME` + INTO PRIMARY_KEY_COLUMN_NAME + FROM `information_schema`.`COLUMNS` + WHERE `TABLE_SCHEMA` = (SELECT IFNULL(SCHEMA_NAME_ARGUMENT, SCHEMA())) + AND `TABLE_NAME` = TABLE_NAME_ARGUMENT + AND `COLUMN_NAME` = COLUMN_NAME_ARGUMENT + AND `COLUMN_TYPE` LIKE '%int%' + AND `COLUMN_KEY` = 'PRI'; + SET SQL_EXP = CONCAT('ALTER TABLE `', (SELECT IFNULL(SCHEMA_NAME_ARGUMENT, SCHEMA())), '`.`', TABLE_NAME_ARGUMENT, '` MODIFY COLUMN `', PRIMARY_KEY_COLUMN_NAME, '` ', PRIMARY_KEY_TYPE, ' NOT NULL AUTO_INCREMENT;'); + SET @SQL_EXP = SQL_EXP; + PREPARE SQL_EXP_EXECUTE FROM @SQL_EXP; + EXECUTE SQL_EXP_EXECUTE; + DEALLOCATE PREPARE SQL_EXP_EXECUTE; + END IF; +END; +CALL POMELO_BEFORE_DROP_PRIMARY_KEY(NULL, 'IceCreams'); +ALTER TABLE `IceCreams` DROP PRIMARY KEY; + +ALTER TABLE `IceCreams` ADD `IceCreamId` int NOT NULL AUTO_INCREMENT; + +INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`) +VALUES ('00000000000002_Migration2', '7.0.0-test'); + +DROP PROCEDURE `POMELO_BEFORE_DROP_PRIMARY_KEY`; +DROP PROCEDURE `POMELO_AFTER_ADD_PRIMARY_KEY`; +COMMIT; + + """, Sql, ignoreLineEndingDifferences: true); @@ -649,6 +777,83 @@ protected override void Down(MigrationBuilder migrationBuilder) #endregion MigrationPrimaryKeyChange + public class MigrationPrimaryKeyChangeFromStringToIntContext : DbContext + { + public MigrationPrimaryKeyChangeFromStringToIntContext(DbContextOptions options) + : base(options) + { + } + + public static class Migrations + { + [DbContext(typeof(MigrationPrimaryKeyChangeFromStringToIntContext))] + [Migration("00000000000001_Migration1")] + private class Migration1 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + MigrationsInfrastructureFixtureBase.ActiveProvider = migrationBuilder.ActiveProvider; + + migrationBuilder + .CreateTable( + name: "IceCreams", + columns: table => new + { + Name = table.Column(type: "varchar(32)", maxLength: 32, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IceCreams", x => x.Name); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + => migrationBuilder.DropTable("IceCreams"); + } + + [DbContext(typeof(MigrationPrimaryKeyChangeFromStringToIntContext))] + [Migration("00000000000002_Migration2")] + private class Migration2 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropPrimaryKey( + name: "PK_IceCreams", + table: "IceCreams"); + + migrationBuilder.AddColumn( + name: "IceCreamId", + table: "IceCreams", + type: "int", + nullable: false, + defaultValue: 0) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn); + + migrationBuilder.AddPrimaryKey( + name: "PK_IceCreams", + table: "IceCreams", + column: "IceCreamId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropPrimaryKey( + name: "PK_IceCreams", + table: "IceCreams"); + + migrationBuilder.DropColumn( + name: "IceCreamId", + table: "IceCreams"); + + migrationBuilder.AddPrimaryKey( + name: "PK_IceCreams", + table: "IceCreams", + column: "Name"); + } + } + } + } + #region MigrationDropPrimaryKeyWithRecreatingForeignKeys public class MigrationDropPrimaryKeyWithRecreatingForeignKeysContext : PoolableDbContext