Skip to content

Commit

Permalink
Migrations: Generate transaction statements in SQL script
Browse files Browse the repository at this point in the history
Fixes #7681
  • Loading branch information
bricelam committed Aug 10, 2020
1 parent 9346ff8 commit 2bdf741
Show file tree
Hide file tree
Showing 21 changed files with 472 additions and 41 deletions.
9 changes: 8 additions & 1 deletion src/EFCore.Design/Design/OperationExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ public class ScriptMigration : OperationBase
/// <para><c>fromMigration</c>--The starting migration. Defaults to <see cref="Migration.InitialDatabase" />.</para>
/// <para><c>toMigration</c>--The ending migration. Defaults to the last migration.</para>
/// <para><c>idempotent</c>--Generate a script that can be used on a database at any migration.</para>
/// <para><c>noTransactions</c>--Don't generate SQL transaction statements.</para>
/// <para><c>contextType</c>--The <see cref="DbContext" /> to use.</para>
/// </summary>
/// <param name="executor"> The operation executor. </param>
Expand All @@ -286,23 +287,29 @@ public ScriptMigration(
var fromMigration = (string)args["fromMigration"];
var toMigration = (string)args["toMigration"];
var idempotent = (bool)args["idempotent"];
var noTransactions = (bool)(args["noTransactions"] ?? false);
var contextType = (string)args["contextType"];

Execute(() => executor.ScriptMigrationImpl(fromMigration, toMigration, idempotent, contextType));
Execute(() => executor.ScriptMigrationImpl(fromMigration, toMigration, idempotent, noTransactions, contextType));
}
}

private string ScriptMigrationImpl(
[CanBeNull] string fromMigration,
[CanBeNull] string toMigration,
bool idempotent,
bool noTransactions,
[CanBeNull] string contextType)
{
var options = MigrationsSqlGenerationOptions.Default;
if (idempotent)
{
options |= MigrationsSqlGenerationOptions.Idempotent;
}
if (noTransactions)
{
options |= MigrationsSqlGenerationOptions.NoTransactions;
}

return MigrationsOperations.ScriptMigration(
fromMigration,
Expand Down
68 changes: 62 additions & 6 deletions src/EFCore.Relational/Migrations/Internal/Migrator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ public virtual string GenerateScript(
options |= MigrationsSqlGenerationOptions.Script;

var idempotent = options.HasFlag(MigrationsSqlGenerationOptions.Idempotent);
var noTransactions = options.HasFlag(MigrationsSqlGenerationOptions.NoTransactions);

IEnumerable<string> appliedMigrations;
if (string.IsNullOrEmpty(fromMigration)
Expand Down Expand Up @@ -335,10 +336,13 @@ public virtual string GenerateScript(
if (fromMigration == Migration.InitialDatabase
|| string.IsNullOrEmpty(fromMigration))
{
builder.AppendLine(_historyRepository.GetCreateIfNotExistsScript());
builder.Append(_sqlGenerationHelper.BatchTerminator);
builder
.Append(_historyRepository.GetCreateIfNotExistsScript())
.Append(_sqlGenerationHelper.BatchTerminator);
}

var transactionStarted = false;

for (var i = 0; i < migrationsToRevert.Count; i++)
{
var migration = migrationsToRevert[i];
Expand All @@ -350,6 +354,24 @@ public virtual string GenerateScript(

foreach (var command in GenerateDownSql(migration, previousMigration, options))
{
if (!noTransactions)
{
if (!transactionStarted && !command.TransactionSuppressed)
{
builder
.AppendLine(_sqlGenerationHelper.StartTransaction)
.Append(_sqlGenerationHelper.BatchTerminator);
transactionStarted = true;
}
if (transactionStarted && command.TransactionSuppressed)
{
builder
.AppendLine(_sqlGenerationHelper.Commit)
.Append(_sqlGenerationHelper.BatchTerminator);
transactionStarted = false;
}
}

if (idempotent)
{
builder.AppendLine(_historyRepository.GetBeginIfExistsScript(migration.GetId()));
Expand All @@ -358,15 +380,23 @@ public virtual string GenerateScript(
builder.AppendLines(command.CommandText);
}

builder.AppendLine(_historyRepository.GetEndIfScript());
builder.Append(_historyRepository.GetEndIfScript());
}
else
{
builder.AppendLine(command.CommandText);
builder.Append(command.CommandText);
}

builder.Append(_sqlGenerationHelper.BatchTerminator);
}

if (!noTransactions && transactionStarted)
{
builder
.AppendLine(_sqlGenerationHelper.Commit)
.Append(_sqlGenerationHelper.BatchTerminator);
transactionStarted = false;
}
}

foreach (var migration in migrationsToApply)
Expand All @@ -375,6 +405,24 @@ public virtual string GenerateScript(

foreach (var command in GenerateUpSql(migration, options))
{
if (!noTransactions)
{
if (!transactionStarted && !command.TransactionSuppressed)
{
builder
.AppendLine(_sqlGenerationHelper.StartTransaction)
.Append(_sqlGenerationHelper.BatchTerminator);
transactionStarted = true;
}
if (transactionStarted && command.TransactionSuppressed)
{
builder
.AppendLine(_sqlGenerationHelper.Commit)
.Append(_sqlGenerationHelper.BatchTerminator);
transactionStarted = false;
}
}

if (idempotent)
{
builder.AppendLine(_historyRepository.GetBeginIfNotExistsScript(migration.GetId()));
Expand All @@ -383,15 +431,23 @@ public virtual string GenerateScript(
builder.AppendLines(command.CommandText);
}

builder.AppendLine(_historyRepository.GetEndIfScript());
builder.Append(_historyRepository.GetEndIfScript());
}
else
{
builder.AppendLine(command.CommandText);
builder.Append(command.CommandText);
}

builder.Append(_sqlGenerationHelper.BatchTerminator);
}

if (!noTransactions && transactionStarted)
{
builder
.AppendLine(_sqlGenerationHelper.Commit)
.Append(_sqlGenerationHelper.BatchTerminator);
transactionStarted = false;
}
}

return builder.ToString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ public enum MigrationsSqlGenerationOptions
/// <summary>
/// Generate SQL for an idempotent script.
/// </summary>
Idempotent = 1 << 1
Idempotent = 1 << 1,

/// <summary>
/// Generate SQL for a script without transaction statements.
/// </summary>
NoTransactions = 1 << 2
}
}
10 changes: 10 additions & 0 deletions src/EFCore.Relational/Storage/ISqlGenerationHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ public interface ISqlGenerationHelper
/// </summary>
string BatchTerminator { get; }

/// <summary>
/// Gets the SQL for a START TRANSACTION statement.
/// </summary>
string StartTransaction { get; }

/// <summary>
/// Gets the SQL for a COMMIT statement.
/// </summary>
string Commit { get; }

/// <summary>
/// The default single-line comment prefix.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,13 @@ public RelationalSqlGenerationHelper([NotNull] RelationalSqlGenerationHelperDepe
/// <summary>
/// The terminator to be used for batches of SQL statements.
/// </summary>
public virtual string BatchTerminator => string.Empty;
public virtual string BatchTerminator => Environment.NewLine;

/// <inheritdoc />
public virtual string StartTransaction => "START TRANSACTION" + StatementTerminator;

/// <inheritdoc />
public virtual string Commit => "COMMIT" + StatementTerminator;

/// <summary>
/// The default single-line comment prefix.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ public SqlServerSqlGenerationHelper([NotNull] RelationalSqlGenerationHelperDepen
/// </summary>
public override string BatchTerminator => "GO" + Environment.NewLine + Environment.NewLine;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override string StartTransaction => "BEGIN TRANSACTION" + StatementTerminator;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Text;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Storage;
Expand Down Expand Up @@ -34,6 +35,15 @@ public SqliteSqlGenerationHelper([NotNull] RelationalSqlGenerationHelperDependen
{
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override string StartTransaction
=> "BEGIN TRANSACTION" + StatementTerminator;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down
Loading

0 comments on commit 2bdf741

Please sign in to comment.