Skip to content

Commit

Permalink
Uniquify all variables used in SQL Server migration scripts
Browse files Browse the repository at this point in the history
Fixes #35132
  • Loading branch information
AndriySvyryd committed Nov 27, 2024
1 parent b7a436f commit ebfc7a4
Show file tree
Hide file tree
Showing 3 changed files with 494 additions and 480 deletions.
98 changes: 56 additions & 42 deletions src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace Microsoft.EntityFrameworkCore.Migrations;
public class SqlServerMigrationsSqlGenerator : MigrationsSqlGenerator
{
private IReadOnlyList<MigrationOperation> _operations = null!;
private int _variableCounter;
private int _variableCounter = -1;

private readonly ICommandBatchPreparer _commandBatchPreparer;

Expand Down Expand Up @@ -643,25 +643,23 @@ protected override void Generate(

subBuilder.Append(")");

var historyTableName = operation[SqlServerAnnotationNames.TemporalHistoryTableName] as string;
string historyTable;
if (needsExec)
{
subBuilder
.EndCommand();

var execBody = subBuilder.GetCommandList().Single().CommandText.Replace("'", "''");

var schemaVariable = Uniquify("@historyTableSchema");
builder
.AppendLine("DECLARE @historyTableSchema sysname = SCHEMA_NAME()")
.AppendLine($"DECLARE {schemaVariable} sysname = SCHEMA_NAME()")
.Append("EXEC(N'")
.Append(execBody);
}

var historyTableName = operation[SqlServerAnnotationNames.TemporalHistoryTableName] as string;
string historyTable;
if (needsExec)
{
historyTable = Dependencies.SqlGenerationHelper.DelimitIdentifier(historyTableName!);
tableCreationOptions.Add($"SYSTEM_VERSIONING = ON (HISTORY_TABLE = [' + @historyTableSchema + N'].{historyTable})");
tableCreationOptions.Add($"SYSTEM_VERSIONING = ON (HISTORY_TABLE = [' + {schemaVariable} + N'].{historyTable})");
}
else
{
Expand Down Expand Up @@ -1116,10 +1114,11 @@ protected override void Generate(
{
if (operation[SqlServerAnnotationNames.EditionOptions] is string editionOptions)
{
var dbVariable = Uniquify("@db_name");
builder
.AppendLine("BEGIN")
.AppendLine("DECLARE @db_name nvarchar(max) = DB_NAME();")
.AppendLine("EXEC(N'ALTER DATABASE [' + @db_name + '] MODIFY ( ")
.AppendLine($"DECLARE {dbVariable} nvarchar(max) = DB_NAME();")
.AppendLine($"EXEC(N'ALTER DATABASE [' + {dbVariable} + '] MODIFY ( ")
.Append(editionOptions.Replace("'", "''"))
.AppendLine(" );');")
.AppendLine("END")
Expand All @@ -1128,19 +1127,21 @@ protected override void Generate(

if (operation.Collation != operation.OldDatabase.Collation)
{
var dbVariable = Uniquify("@db_name");
builder
.AppendLine("BEGIN")
.AppendLine("DECLARE @db_name nvarchar(max) = DB_NAME();");
.AppendLine($"DECLARE {dbVariable} nvarchar(max) = DB_NAME();");

var collation = operation.Collation;
if (operation.Collation == null)
{
builder.AppendLine("DECLARE @defaultCollation nvarchar(max) = CAST(SERVERPROPERTY('Collation') AS nvarchar(max));");
var collationVariable = Uniquify("@defaultCollation");
builder.AppendLine($"DECLARE {collationVariable} nvarchar(max) = CAST(SERVERPROPERTY('Collation') AS nvarchar(max));");
collation = "' + " + collationVariable + " + N'";
}

builder
.Append("EXEC(N'ALTER DATABASE [' + @db_name + '] COLLATE ")
.Append(operation.Collation ?? "' + @defaultCollation + N'")
.AppendLine(";');")
.AppendLine($"EXEC(N'ALTER DATABASE [' + {dbVariable} + '] COLLATE {collation};');")
.AppendLine("END")
.AppendLine();
}
Expand All @@ -1167,10 +1168,11 @@ protected override void Generate(

using (builder.Indent())
{
var dbVariable = Uniquify("@db_name");
builder
.AppendLine("BEGIN")
.AppendLine("ALTER DATABASE CURRENT SET AUTO_CLOSE OFF;")
.AppendLine("DECLARE @db_name nvarchar(max) = DB_NAME();")
.AppendLine($"DECLARE {dbVariable} nvarchar(max) = DB_NAME();")
.AppendLine("DECLARE @fg_name nvarchar(max);")
.AppendLine("SELECT TOP(1) @fg_name = [name] FROM [sys].[filegroups] WHERE [type] = N'FX';")
.AppendLine()
Expand All @@ -1180,20 +1182,21 @@ protected override void Generate(
{
builder
.AppendLine("BEGIN")
.AppendLine("SET @fg_name = @db_name + N'_MODFG';")
.AppendLine($"SET @fg_name = {dbVariable} + N'_MODFG';")
.AppendLine("EXEC(N'ALTER DATABASE CURRENT ADD FILEGROUP [' + @fg_name + '] CONTAINS MEMORY_OPTIMIZED_DATA;');")
.AppendLine("END");
}

var pathVariable = Uniquify("@path");
builder
.AppendLine()
.AppendLine("DECLARE @path nvarchar(max);")
.Append("SELECT TOP(1) @path = [physical_name] FROM [sys].[database_files] ")
.AppendLine($"DECLARE {pathVariable} nvarchar(max);")
.Append($"SELECT TOP(1) {pathVariable} = [physical_name] FROM [sys].[database_files] ")
.AppendLine("WHERE charindex('\\', [physical_name]) > 0 ORDER BY [file_id];")
.AppendLine("IF (@path IS NULL)")
.IncrementIndent().AppendLine("SET @path = '\\' + @db_name;").DecrementIndent()
.AppendLine($"IF ({pathVariable} IS NULL)")
.IncrementIndent().AppendLine($"SET {pathVariable} = '\\' + {dbVariable};").DecrementIndent()
.AppendLine()
.AppendLine("DECLARE @filename nvarchar(max) = right(@path, charindex('\\', reverse(@path)) - 1);")
.AppendLine($"DECLARE @filename nvarchar(max) = right({pathVariable}, charindex('\\', reverse({pathVariable})) - 1);")
.AppendLine(
"SET @filename = REPLACE(left(@filename, len(@filename) - charindex('.', reverse(@filename))), '''', '''''') + N'_MOD';")
.AppendLine(
Expand Down Expand Up @@ -1739,10 +1742,11 @@ protected virtual void Transfer(
{
var stringTypeMapping = Dependencies.TypeMappingSource.GetMapping(typeof(string));

var schemaVariable = Uniquify("@defaultSchema");
builder
.AppendLine("DECLARE @defaultSchema sysname = SCHEMA_NAME();")
.AppendLine($"DECLARE {schemaVariable} sysname = SCHEMA_NAME();")
.Append("EXEC(")
.Append("N'ALTER SCHEMA [' + @defaultSchema + ")
.Append($"N'ALTER SCHEMA [' + {schemaVariable} + ")
.Append(
stringTypeMapping.GenerateSqlLiteral(
"] TRANSFER " + Dependencies.SqlGenerationHelper.DelimitIdentifier(name, schema) + ";"))
Expand Down Expand Up @@ -1908,7 +1912,7 @@ protected virtual void DropDefaultConstraint(
{
var stringTypeMapping = Dependencies.TypeMappingSource.GetMapping(typeof(string));

var variable = "@var" + _variableCounter++;
var variable = Uniquify("@var");

builder
.Append("DECLARE ")
Expand Down Expand Up @@ -2039,18 +2043,18 @@ protected virtual void AddDescription(
string? column = null,
bool omitVariableDeclarations = false)
{
string schemaLiteral;
var schemaLiteral = Uniquify("@defaultSchema", increase: !omitVariableDeclarations);
var descriptionVariable = Uniquify("@description", increase: false);

if (schema == null)
{
if (!omitVariableDeclarations)
{
builder.Append("DECLARE @defaultSchema AS sysname")
builder.Append($"DECLARE {schemaLiteral} AS sysname")
.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
builder.Append("SET @defaultSchema = SCHEMA_NAME()")
builder.Append($"SET {schemaLiteral} = SCHEMA_NAME()")
.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
}

schemaLiteral = "@defaultSchema";
}
else
{
Expand All @@ -2059,16 +2063,15 @@ protected virtual void AddDescription(

if (!omitVariableDeclarations)
{
builder.Append("DECLARE @description AS sql_variant")
builder.Append($"DECLARE {descriptionVariable} AS sql_variant")
.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
}

builder.Append("SET @description = ")
.Append(Literal(description))
builder.Append($"SET {descriptionVariable} = {Literal(description)}")
.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
builder
.Append("EXEC sp_addextendedproperty 'MS_Description', ")
.Append("@description")
.Append(descriptionVariable)
.Append(", 'SCHEMA', ")
.Append(schemaLiteral)
.Append(", 'TABLE', ")
Expand Down Expand Up @@ -2221,18 +2224,17 @@ protected virtual void DropDescription(
{
var stringTypeMapping = Dependencies.TypeMappingSource.GetMapping(typeof(string));

string schemaLiteral;
var schemaLiteral = Uniquify("@defaultSchema", increase: !omitVariableDeclarations);
var descriptionVariable = Uniquify("@description", increase: false);
if (schema == null)
{
if (!omitVariableDeclarations)
{
builder.Append("DECLARE @defaultSchema AS sysname")
builder.Append($"DECLARE {schemaLiteral} AS sysname")
.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
builder.Append("SET @defaultSchema = SCHEMA_NAME()")
builder.Append($"SET {schemaLiteral} = SCHEMA_NAME()")
.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
}

schemaLiteral = "@defaultSchema";
}
else
{
Expand All @@ -2241,7 +2243,7 @@ protected virtual void DropDescription(

if (!omitVariableDeclarations)
{
builder.Append("DECLARE @description AS sql_variant")
builder.Append($"DECLARE {descriptionVariable} AS sql_variant")
.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
}

Expand Down Expand Up @@ -2351,6 +2353,16 @@ private static bool HasDifferences(IEnumerable<IAnnotation> source, IEnumerable<
return count != targetAnnotations.Count;
}

private string Uniquify(string variableName, bool increase = true)
{
if (increase)
{
_variableCounter++;
}

return _variableCounter == 0 ? variableName : variableName + _variableCounter;
}

private IReadOnlyList<MigrationOperation> RewriteOperations(
IReadOnlyList<MigrationOperation> migrationOperations,
IModel? model,
Expand Down Expand Up @@ -3043,10 +3055,12 @@ void EnableVersioning(string table, string? schema, string historyTableName, str
{
var stringBuilder = new StringBuilder();

string? schemaVariable = null;
if (historyTableSchema == null)
{
schemaVariable = Uniquify("@historyTableSchema");
// need to run command using EXEC to inject default schema
stringBuilder.AppendLine("DECLARE @historyTableSchema sysname = SCHEMA_NAME()");
stringBuilder.AppendLine($"DECLARE {schemaVariable} sysname = SCHEMA_NAME()");
stringBuilder.Append("EXEC(N'");
}

Expand All @@ -3065,7 +3079,7 @@ void EnableVersioning(string table, string? schema, string historyTableName, str
else
{
stringBuilder.AppendLine(
$" SET (SYSTEM_VERSIONING = ON (HISTORY_TABLE = [' + @historyTableSchema + '].{historyTable}))')");
$" SET (SYSTEM_VERSIONING = ON (HISTORY_TABLE = [' + {schemaVariable} + '].{historyTable}))')");
}

operations.Add(
Expand Down
Loading

0 comments on commit ebfc7a4

Please sign in to comment.