Enable Detach Database in database handler (#2110)

This commit is contained in:
Cory Rivera
2023-06-22 17:28:41 -07:00
committed by GitHub
parent 2052b597c9
commit 9d0d4b0cae
5 changed files with 332 additions and 32 deletions

View File

@@ -4,6 +4,7 @@
//
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Data.SqlClient;
@@ -13,6 +14,7 @@ using Microsoft.SqlTools.ServiceLayer.Admin;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
using Microsoft.SqlTools.ServiceLayer.ObjectManagement;
using Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using NUnit.Framework;
using static Microsoft.SqlTools.ServiceLayer.Admin.AzureSqlDbHelper;
@@ -201,7 +203,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
{ AzureEdition.Hyperscale, "HS_Gen5_2" }
};
Assert.That(actualLevelsMap.Count, Is.EqualTo(expectedDefaults.Count), "Did not get expected number of editions for DatabaseHandler's service levels");
foreach(AzureEdition edition in expectedDefaults.Keys)
foreach (AzureEdition edition in expectedDefaults.Keys)
{
if (AzureSqlDbHelper.TryGetServiceObjectiveInfo(edition, out var expectedLevelInfo))
{
@@ -211,7 +213,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
var expectedDefaultIndex = expectedLevelInfo.Key;
var expectedDefault = expectedServiceLevels[expectedDefaultIndex];
var actualDefault = actualServiceLevels[0];
var actualDefault = actualServiceLevels[0];
Assert.That(actualDefault, Is.EqualTo(expectedDefault), "Did not get expected default SLO for edition '{0}'", edition.DisplayName);
}
else
@@ -240,7 +242,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
{ AzureEdition.Hyperscale, "0MB" }
};
Assert.That(actualSizesMap.Count, Is.EqualTo(expectedDefaults.Count), "Did not get expected number of editions for DatabaseHandler's max sizes");
foreach(AzureEdition edition in expectedDefaults.Keys)
foreach (AzureEdition edition in expectedDefaults.Keys)
{
if (AzureSqlDbHelper.TryGetDatabaseSizeInfo(edition, out var expectedSizeInfo))
{
@@ -250,7 +252,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
var expectedDefaultIndex = expectedSizeInfo.Key;
var expectedDefault = expectedSizes[expectedDefaultIndex];
var actualDefault = actualSizes[0];
var actualDefault = actualSizes[0];
Assert.That(actualDefault, Is.EqualTo(expectedDefault.ToString()), "Did not get expected default size for edition '{0}'", edition.DisplayName);
}
else
@@ -304,6 +306,132 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
}
}
[Test]
public async Task DetachDatabaseTest()
{
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master");
using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connectionResult.ConnectionInfo))
{
var server = new Server(new ServerConnection(sqlConn));
var testDatabase = ObjectManagementTestUtils.GetTestDatabaseInfo();
var objUrn = ObjectManagementTestUtils.GetDatabaseURN(testDatabase.Name);
await ObjectManagementTestUtils.DropObject(connectionResult.ConnectionInfo.OwnerUri, objUrn);
try
{
// Create database to test with
var parametersForCreation = ObjectManagementTestUtils.GetInitializeViewRequestParams(connectionResult.ConnectionInfo.OwnerUri, "master", true, SqlObjectType.Database, "", "");
await ObjectManagementTestUtils.SaveObject(parametersForCreation, testDatabase);
Assert.That(DatabaseExists(testDatabase.Name!, server), $"Expected database '{testDatabase.Name}' was not created succesfully");
var handler = new DatabaseHandler(ConnectionService.Instance);
var connectionUri = connectionResult.ConnectionInfo.OwnerUri;
var detachParams = new DetachDatabaseRequestParams()
{
ConnectionUri = connectionUri,
ObjectUrn = objUrn,
DropConnections = true,
UpdateStatistics = true,
GenerateScript = false
};
// Get databases's files so we can reattach it later before dropping it
var fileCollection = new StringCollection();
var smoDatabase = server.GetSmoObject(objUrn) as Database;
foreach (FileGroup fileGroup in smoDatabase!.FileGroups)
{
foreach (DataFile file in fileGroup.Files)
{
fileCollection.Add(file.FileName);
}
}
foreach (LogFile file in smoDatabase.LogFiles)
{
fileCollection.Add(file.FileName);
}
var script = handler.Detach(detachParams);
Assert.That(script, Is.Empty, "Should only return an empty string if GenerateScript is false");
server.Databases.Refresh();
Assert.That(DatabaseExists(testDatabase.Name!, server), Is.False, $"Expected database '{testDatabase.Name}' was not detached succesfully");
server.AttachDatabase(testDatabase.Name, fileCollection);
server.Databases.Refresh();
Assert.That(DatabaseExists(testDatabase.Name!, server), $"Expected database '{testDatabase.Name}' was not re-attached succesfully");
}
finally
{
DropDatabase(server, testDatabase.Name!);
}
}
}
[Test]
public async Task DetachDatabaseScriptTest()
{
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master");
using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connectionResult.ConnectionInfo))
{
var server = new Server(new ServerConnection(sqlConn));
var testDatabase = ObjectManagementTestUtils.GetTestDatabaseInfo();
var objUrn = ObjectManagementTestUtils.GetDatabaseURN(testDatabase.Name);
await ObjectManagementTestUtils.DropObject(connectionResult.ConnectionInfo.OwnerUri, objUrn);
try
{
// Create database to test with
var parametersForCreation = ObjectManagementTestUtils.GetInitializeViewRequestParams(connectionResult.ConnectionInfo.OwnerUri, "master", true, SqlObjectType.Database, "", "");
await ObjectManagementTestUtils.SaveObject(parametersForCreation, testDatabase);
var handler = new DatabaseHandler(ConnectionService.Instance);
var connectionUri = connectionResult.ConnectionInfo.OwnerUri;
// Default use case
var detachParams = new DetachDatabaseRequestParams()
{
ConnectionUri = connectionUri,
ObjectUrn = objUrn,
DropConnections = false,
UpdateStatistics = false,
GenerateScript = true
};
var expectedDetachScript = $"EXEC master.dbo.sp_detach_db @dbname = N'{testDatabase.Name}'";
var expectedAlterScript = $"ALTER DATABASE [{testDatabase.Name}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE";
var expectedStatsScript = $"EXEC master.dbo.sp_detach_db @dbname = N'{testDatabase.Name}', @skipchecks = 'false'";
var actualScript = handler.Detach(detachParams);
Assert.That(actualScript, Does.Contain(expectedDetachScript).IgnoreCase);
// Drop connections only
detachParams.DropConnections = true;
actualScript = handler.Detach(detachParams);
Assert.That(actualScript, Does.Contain(expectedDetachScript).IgnoreCase);
Assert.That(actualScript, Does.Contain(expectedAlterScript).IgnoreCase);
// Update statistics only
detachParams.DropConnections = false;
detachParams.UpdateStatistics = true;
actualScript = handler.Detach(detachParams);
Assert.That(actualScript, Does.Contain(expectedStatsScript).IgnoreCase);
// Both drop and update
detachParams.DropConnections = true;
actualScript = handler.Detach(detachParams);
Assert.That(actualScript, Does.Contain(expectedAlterScript).IgnoreCase);
Assert.That(actualScript, Does.Contain(expectedStatsScript).IgnoreCase);
}
finally
{
DropDatabase(server, testDatabase.Name!);
}
}
}
private bool DatabaseExists(string dbName, Server server)
{
server.Databases.Refresh();