Skip to content

Commit 166e7da

Browse files
committed
Fix making column required on SQL Server with idempotent migrations (dotnet#29619)
Fixes dotnet#29530 (cherry picked from commit 6b3b852)
1 parent bf20e32 commit 166e7da

File tree

3 files changed

+80
-11
lines changed

3 files changed

+80
-11
lines changed

src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs

+42-11
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ public class SqlServerMigrationsSqlGenerator : MigrationsSqlGenerator
3535

3636
private readonly ICommandBatchPreparer _commandBatchPreparer;
3737

38+
private static readonly bool QuirkEnabled29619
39+
= AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue29619", out var enabled) && enabled;
40+
3841
/// <summary>
3942
/// Creates a new <see cref="SqlServerMigrationsSqlGenerator" /> instance.
4043
/// </summary>
@@ -369,17 +372,45 @@ protected override void Generate(
369372
defaultValueSql = typeMapping.GenerateSqlLiteral(operation.DefaultValue);
370373
}
371374

372-
builder
373-
.Append("UPDATE ")
374-
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Table, operation.Schema))
375-
.Append(" SET ")
376-
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name))
377-
.Append(" = ")
378-
.Append(defaultValueSql)
379-
.Append(" WHERE ")
380-
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name))
381-
.Append(" IS NULL")
382-
.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
375+
if (QuirkEnabled29619)
376+
{
377+
builder
378+
.Append("UPDATE ")
379+
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Table, operation.Schema))
380+
.Append(" SET ")
381+
.Append(defaultValueSql)
382+
.Append(" WHERE ")
383+
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name))
384+
.Append(" IS NULL")
385+
.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
386+
}
387+
else
388+
{
389+
var updateBuilder = new StringBuilder()
390+
.Append("UPDATE ")
391+
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Table, operation.Schema))
392+
.Append(" SET ")
393+
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name))
394+
.Append(" = ")
395+
.Append(defaultValueSql)
396+
.Append(" WHERE ")
397+
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name))
398+
.Append(" IS NULL");
399+
400+
if (Options.HasFlag(MigrationsSqlGenerationOptions.Idempotent))
401+
{
402+
builder
403+
.Append("EXEC(N'")
404+
.Append(updateBuilder.ToString().TrimEnd('\n', '\r', ';').Replace("'", "''"))
405+
.Append("')");
406+
}
407+
else
408+
{
409+
builder.Append(updateBuilder.ToString());
410+
}
411+
412+
builder.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
413+
}
383414
}
384415

385416
if (alterStatementNeeded)

test/EFCore.Relational.Specification.Tests/Migrations/MigrationsSqlGeneratorTestBase.cs

+3
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,9 @@ protected MigrationsSqlGeneratorTestBase(
750750
ContextOptions = options;
751751
}
752752

753+
protected virtual void Generate(MigrationOperation operation, MigrationsSqlGenerationOptions options)
754+
=> Generate(null, new[] { operation }, options);
755+
753756
protected virtual void Generate(params MigrationOperation[] operation)
754757
=> Generate(null, operation);
755758

test/EFCore.SqlServer.FunctionalTests/Migrations/SqlServerMigrationsSqlGeneratorTest.cs

+35
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,41 @@ public virtual void CreateIndex_generates_exec_when_legacy_filter_and_idempotent
11801180
""");
11811181
}
11821182

1183+
[ConditionalFact]
1184+
public virtual void AlterColumn_make_required_with_idempotent()
1185+
{
1186+
Generate(
1187+
new AlterColumnOperation
1188+
{
1189+
Table = "Person",
1190+
Name = "Name",
1191+
ClrType = typeof(string),
1192+
IsNullable = false,
1193+
DefaultValue = "",
1194+
OldColumn = new AddColumnOperation
1195+
{
1196+
Table = "Person",
1197+
Name = "Name",
1198+
ClrType = typeof(string),
1199+
IsNullable = true
1200+
}
1201+
},
1202+
MigrationsSqlGenerationOptions.Idempotent);
1203+
1204+
AssertSql(
1205+
"""
1206+
DECLARE @var0 sysname;
1207+
SELECT @var0 = [d].[name]
1208+
FROM [sys].[default_constraints] [d]
1209+
INNER JOIN [sys].[columns] [c] ON [d].[parent_column_id] = [c].[column_id] AND [d].[parent_object_id] = [c].[object_id]
1210+
WHERE ([d].[parent_object_id] = OBJECT_ID(N'[Person]') AND [c].[name] = N'Name');
1211+
IF @var0 IS NOT NULL EXEC(N'ALTER TABLE [Person] DROP CONSTRAINT [' + @var0 + '];');
1212+
EXEC(N'UPDATE [Person] SET [Name] = N'''' WHERE [Name] IS NULL');
1213+
ALTER TABLE [Person] ALTER COLUMN [Name] nvarchar(max) NOT NULL;
1214+
ALTER TABLE [Person] ADD DEFAULT N'' FOR [Name];
1215+
""");
1216+
}
1217+
11831218
private static void CreateGotModel(ModelBuilder b)
11841219
=> b.HasDefaultSchema("dbo").Entity(
11851220
"Person", pb =>

0 commit comments

Comments
 (0)