Skip to content

Commit

Permalink
Fix SQL Server erase command when multiple objects dependencies (#208)
Browse files Browse the repository at this point in the history
Delete tests of issue #203
  • Loading branch information
lecaillon committed Feb 22, 2021
1 parent 1cce039 commit 2eac216
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 26 deletions.
9 changes: 8 additions & 1 deletion src/Evolve/Connection/WrappedConnection.cs
Expand Up @@ -70,7 +70,14 @@ public void Commit()
throw new InvalidOperationException(NoActiveTransaction);
}

CurrentTx.Commit();
try
{
CurrentTx.Commit();
}
catch (InvalidOperationException ex) when (ex.StackTrace?.Contains("ZombieCheck()") ?? false)
{ // SQL Server hack to avoid : Transaction has completed; it is no longer usable (ZombieCheck).
}

ClearTransaction();
}

Expand Down
89 changes: 64 additions & 25 deletions src/Evolve/Dialect/SQLServer/SQLServerSchema.cs
Expand Up @@ -61,8 +61,9 @@ public override bool Erase()
DropProcedures();
DropViews();
DropSystemVersioning(); // SQLServerVersion >= 13
DropTables();
DropTables(throwOnError: false);
DropFunctions(throwOnError: true);
DropTables(throwOnError: true);
DropTypes();
DropSynonyms();
DropSequences(); // SQLServerVersion >= 11
Expand All @@ -77,9 +78,9 @@ protected void DropForeignKeys()
"WHERE CONSTRAINT_TYPE IN ('FOREIGN KEY','CHECK') " +
$"AND TABLE_SCHEMA = '{Name}'";

_wrappedConnection.QueryForList(sql, (r) => new { RoutineName = r.GetString(0), RoutineType = r.GetString(1) }).ToList().ForEach(x =>
_wrappedConnection.QueryForList(sql, (r) => new { Table = r.GetString(0), Constraint = r.GetString(1) }).ToList().ForEach(x =>
{
_wrappedConnection.ExecuteNonQuery($"ALTER TABLE [{Name}].[{x.RoutineName}] DROP CONSTRAINT [{x.RoutineType}]");
_wrappedConnection.ExecuteNonQuery($"ALTER TABLE [{Name}].[{x.Table}] DROP CONSTRAINT [{x.Constraint}]");
});
}

Expand All @@ -91,9 +92,9 @@ protected void DropDefaultConstraints()
"INNER JOIN sys.schemas s ON s.schema_id = t.schema_id " +
$"WHERE s.name = '{Name}'";

_wrappedConnection.QueryForList(sql, (r) => new { RoutineName = r.GetString(0), RoutineType = r.GetString(1) }).ToList().ForEach(x =>
_wrappedConnection.QueryForList(sql, (r) => new { Table = r.GetString(0), Constraint = r.GetString(1) }).ToList().ForEach(x =>
{
_wrappedConnection.ExecuteNonQuery($"ALTER TABLE [{Name}].[{x.RoutineName}] DROP CONSTRAINT [{x.RoutineType}]");
_wrappedConnection.ExecuteNonQuery($"ALTER TABLE [{Name}].[{x.Table}] DROP CONSTRAINT [{x.Constraint}]");
});
}

Expand All @@ -109,9 +110,9 @@ protected void DropProcedures()
protected void DropViews()
{
string sql = $"SELECT table_name FROM INFORMATION_SCHEMA.VIEWS WHERE table_schema = '{Name}'";
_wrappedConnection.QueryForListOfString(sql).ToList().ForEach(vw =>
_wrappedConnection.QueryForListOfString(sql).ToList().ForEach(view =>
{
_wrappedConnection.ExecuteNonQuery($"DROP VIEW [{Name}].[{vw}]");
_wrappedConnection.ExecuteNonQuery($"DROP VIEW [{Name}].[{view}]");
});
}

Expand All @@ -134,12 +135,35 @@ private void DropSystemVersioning()
});
}

protected void DropTables()
protected void DropTables(bool throwOnError)
{
GetTables().ForEach(t =>
List<Exception> exceptions;
int droppedCount;

do
{
_wrappedConnection.ExecuteNonQuery($"DROP TABLE [{Name}].[{t}]");
});
exceptions = new();
droppedCount = 0;

GetTables().ForEach(table =>
{
try
{
_wrappedConnection.ExecuteNonQuery($"DROP TABLE [{Name}].[{table}]");
droppedCount += 1;
}
catch (Exception ex)
{
exceptions.Add(ex);
}
});

} while (droppedCount > 0);

if (exceptions.Any() && throwOnError)
{
throw exceptions.First();
}
}

protected void DropComputedColumns(bool throwOnError)
Expand All @@ -150,7 +174,7 @@ protected void DropComputedColumns(bool throwOnError)
{
try
{
_wrappedConnection.ExecuteNonQuery($"ALTER TABLE [{Name}].[{t}] DROP COLUMN [{Name}].[{c}]");
_wrappedConnection.ExecuteNonQuery($"ALTER TABLE [{Name}].[{t}] DROP COLUMN [{c}]");
}
catch
{
Expand All @@ -166,20 +190,33 @@ protected void DropComputedColumns(bool throwOnError)
protected void DropFunctions(bool throwOnError)
{
string sql = $"SELECT routine_name FROM INFORMATION_SCHEMA.ROUTINES WHERE routine_schema = '{Name}' AND routine_type = 'FUNCTION' ORDER BY created DESC";
_wrappedConnection.QueryForListOfString(sql).ToList().ForEach(fn =>
List<Exception> exceptions;
int droppedCount;

do
{
try
{
_wrappedConnection.ExecuteNonQuery($"DROP FUNCTION [{Name}].[{fn}]");
}
catch
exceptions = new();
droppedCount = 0;

_wrappedConnection.QueryForListOfString(sql).ToList().ForEach(function =>
{
if (throwOnError)
try
{
throw;
_wrappedConnection.ExecuteNonQuery($"DROP FUNCTION [{Name}].[{function}]");
droppedCount += 1;
}
}
});
catch (Exception ex)
{
exceptions.Add(ex);
}
});

} while (droppedCount > 0);

if (exceptions.Any() && throwOnError)
{
throw exceptions.First();
}
}

protected void DropTypes()
Expand Down Expand Up @@ -216,9 +253,11 @@ protected void DropSequences()

private List<string> GetTables()
=> _wrappedConnection.QueryForListOfString("SELECT table_name " +
"FROM INFORMATION_SCHEMA.TABLES " +
"WHERE table_type='BASE TABLE' " +
$"AND table_schema = '{Name}'").ToList();
"FROM INFORMATION_SCHEMA.TABLES t1 " +
"INNER JOIN sys.tables t2 ON t1.table_name = t2.name " +
"WHERE table_type = 'BASE TABLE' " +
$"AND table_schema = '{Name}' " +
"ORDER BY create_date DESC").ToList();

private long SQLServerVersion
{
Expand Down

0 comments on commit 2eac216

Please sign in to comment.