mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-17 02:51:45 -05:00
Add Attach Database functionality to Object Management Service (#2193)
This commit is contained in:
@@ -8,10 +8,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
using Microsoft.SqlServer.Management.Common;
|
||||||
using Microsoft.SqlServer.Management.Smo;
|
using Microsoft.SqlServer.Management.Smo;
|
||||||
using Microsoft.SqlTools.Hosting.Protocol;
|
using Microsoft.SqlTools.Hosting.Protocol;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Admin.Contracts;
|
using Microsoft.SqlTools.ServiceLayer.Admin.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Management;
|
using Microsoft.SqlTools.ServiceLayer.Management;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||||
@@ -71,6 +74,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Admin
|
|||||||
serviceHost.SetRequestHandler(CreateLoginRequest.Type, HandleCreateLoginRequest, true);
|
serviceHost.SetRequestHandler(CreateLoginRequest.Type, HandleCreateLoginRequest, true);
|
||||||
serviceHost.SetRequestHandler(DefaultDatabaseInfoRequest.Type, HandleDefaultDatabaseInfoRequest, true);
|
serviceHost.SetRequestHandler(DefaultDatabaseInfoRequest.Type, HandleDefaultDatabaseInfoRequest, true);
|
||||||
serviceHost.SetRequestHandler(GetDatabaseInfoRequest.Type, HandleGetDatabaseInfoRequest, true);
|
serviceHost.SetRequestHandler(GetDatabaseInfoRequest.Type, HandleGetDatabaseInfoRequest, true);
|
||||||
|
serviceHost.SetRequestHandler(GetDataFolderRequest.Type, HandleGetDataFolderRequest, true);
|
||||||
|
serviceHost.SetRequestHandler(GetAssociatedFilesRequest.Type, HandleGetAssociatedFilesRequest, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -153,6 +158,74 @@ namespace Microsoft.SqlTools.ServiceLayer.Admin
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handle get database info request
|
||||||
|
/// </summary>
|
||||||
|
internal static async Task HandleGetDataFolderRequest(
|
||||||
|
GetDataFolderParams databaseParams,
|
||||||
|
RequestContext<string> requestContext)
|
||||||
|
{
|
||||||
|
Func<Task> requestHandler = async () =>
|
||||||
|
{
|
||||||
|
ConnectionInfo connInfo;
|
||||||
|
AdminService.ConnectionServiceInstance.TryFindConnection(
|
||||||
|
databaseParams.ConnectionUri,
|
||||||
|
out connInfo);
|
||||||
|
using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo))
|
||||||
|
{
|
||||||
|
// Connection gets disconnected when backup is done
|
||||||
|
ServerConnection serverConnection = new ServerConnection(sqlConn);
|
||||||
|
var dataFolder = CommonUtilities.GetDefaultDataFolder(serverConnection);
|
||||||
|
await requestContext.SendResult(dataFolder);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Task task = Task.Run(async () => await requestHandler()).ContinueWithOnFaulted(async t =>
|
||||||
|
{
|
||||||
|
// Get innermost exception to get original error message
|
||||||
|
Exception ex = t.Exception;
|
||||||
|
while (ex.InnerException != null)
|
||||||
|
{
|
||||||
|
ex = ex.InnerException;
|
||||||
|
};
|
||||||
|
await requestContext.SendError(ex.Message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handle get associated database files request
|
||||||
|
/// </summary>
|
||||||
|
internal static async Task HandleGetAssociatedFilesRequest(
|
||||||
|
GetAssociatedFilesParams databaseParams,
|
||||||
|
RequestContext<string[]> requestContext)
|
||||||
|
{
|
||||||
|
Func<Task> requestHandler = async () =>
|
||||||
|
{
|
||||||
|
ConnectionInfo connInfo;
|
||||||
|
AdminService.ConnectionServiceInstance.TryFindConnection(
|
||||||
|
databaseParams.ConnectionUri,
|
||||||
|
out connInfo);
|
||||||
|
using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo))
|
||||||
|
{
|
||||||
|
// Connection gets disconnected when backup is done
|
||||||
|
ServerConnection serverConnection = new ServerConnection(sqlConn);
|
||||||
|
var files = CommonUtilities.GetAssociatedFilePaths(serverConnection, databaseParams.PrimaryFilePath);
|
||||||
|
await requestContext.SendResult(files);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Task task = Task.Run(async () => await requestHandler()).ContinueWithOnFaulted(async t =>
|
||||||
|
{
|
||||||
|
// Get innermost exception to get original error message
|
||||||
|
Exception ex = t.Exception;
|
||||||
|
while (ex.InnerException != null)
|
||||||
|
{
|
||||||
|
ex = ex.InnerException;
|
||||||
|
};
|
||||||
|
await requestContext.SendError(ex.Message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Return database info for a specific database
|
/// Return database info for a specific database
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.Admin.Contracts
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Params for a get database info request
|
||||||
|
/// </summar>
|
||||||
|
public class GetAssociatedFilesParams
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// URI identifier for the connection to the target server.
|
||||||
|
/// </summary>
|
||||||
|
public string ConnectionUri { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The file path for the primary file that we want to get the associated files for.
|
||||||
|
/// </summary>
|
||||||
|
public string PrimaryFilePath { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get database info request mapping
|
||||||
|
/// </summary>
|
||||||
|
public class GetAssociatedFilesRequest
|
||||||
|
{
|
||||||
|
public static readonly
|
||||||
|
RequestType<GetAssociatedFilesParams, string[]> Type =
|
||||||
|
RequestType<GetAssociatedFilesParams, string[]>.Create("admin/getassociatedfiles");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.Admin.Contracts
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Params for a get database info request
|
||||||
|
/// </summar>
|
||||||
|
public class GetDataFolderParams
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// URI identifier for the connection to get the server folder info for
|
||||||
|
/// </summary>
|
||||||
|
public string ConnectionUri { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get database info request mapping
|
||||||
|
/// </summary>
|
||||||
|
public class GetDataFolderRequest
|
||||||
|
{
|
||||||
|
public static readonly
|
||||||
|
RequestType<GetDataFolderParams, string> Type =
|
||||||
|
RequestType<GetDataFolderParams, string>.Create("admin/getdatafolder");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ using Microsoft.SqlServer.Management.Sdk.Sfc;
|
|||||||
using Microsoft.SqlServer.Management.Smo;
|
using Microsoft.SqlServer.Management.Smo;
|
||||||
using SMO = Microsoft.SqlServer.Management.Smo;
|
using SMO = Microsoft.SqlServer.Management.Smo;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Management;
|
using Microsoft.SqlTools.ServiceLayer.Management;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||||
{
|
{
|
||||||
@@ -35,13 +36,13 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
Database,
|
Database,
|
||||||
Files
|
Files
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Backup set type
|
/// Backup set type
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum BackupsetType
|
public enum BackupsetType
|
||||||
{
|
{
|
||||||
BackupsetDatabase,
|
BackupsetDatabase,
|
||||||
BackupsetLog,
|
BackupsetLog,
|
||||||
BackupsetDifferential,
|
BackupsetDifferential,
|
||||||
BackupsetFiles
|
BackupsetFiles
|
||||||
@@ -101,7 +102,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return (RestoreItemLocation+RestoreItemDeviceType.ToString() + IsLogicalDevice.ToString()).GetHashCode();
|
return (RestoreItemLocation + RestoreItemDeviceType.ToString() + IsLogicalDevice.ToString()).GetHashCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,17 +137,17 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
/// <param name="dataContainer"></param>
|
/// <param name="dataContainer"></param>
|
||||||
/// <param name="sqlConnection"></param>
|
/// <param name="sqlConnection"></param>
|
||||||
public CommonUtilities(CDataContainer dataContainer, ServerConnection sqlConnection)
|
public CommonUtilities(CDataContainer dataContainer, ServerConnection sqlConnection)
|
||||||
{
|
{
|
||||||
this.dataContainer = dataContainer;
|
this.dataContainer = dataContainer;
|
||||||
this.sqlConnection = sqlConnection;
|
this.sqlConnection = sqlConnection;
|
||||||
this.excludedDatabases = new ArrayList();
|
this.excludedDatabases = new ArrayList();
|
||||||
this.excludedDatabases.Add("master");
|
this.excludedDatabases.Add("master");
|
||||||
this.excludedDatabases.Add("tempdb");
|
this.excludedDatabases.Add("tempdb");
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetServerVersion()
|
public int GetServerVersion()
|
||||||
{
|
{
|
||||||
return this.dataContainer.Server.Information.Version.Major;
|
return this.dataContainer.Server.Information.Version.Major;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -162,7 +163,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
else if (String.Compare(stringDeviceType, RestoreConstants.Url, StringComparison.OrdinalIgnoreCase) == 0)
|
else if (String.Compare(stringDeviceType, RestoreConstants.Url, StringComparison.OrdinalIgnoreCase) == 0)
|
||||||
{
|
{
|
||||||
return DeviceType.Url;
|
return DeviceType.Url;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return DeviceType.LogicalDevice;
|
return DeviceType.LogicalDevice;
|
||||||
@@ -188,10 +189,10 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
return DeviceType.LogicalDevice;
|
return DeviceType.LogicalDevice;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public BackupDeviceType GetPhisycalDeviceTypeOfLogicalDevice(string deviceName)
|
public BackupDeviceType GetPhisycalDeviceTypeOfLogicalDevice(string deviceName)
|
||||||
{
|
{
|
||||||
Enumerator enumerator = new Enumerator();
|
Enumerator enumerator = new Enumerator();
|
||||||
Request request = new Request();
|
Request request = new Request();
|
||||||
DataSet dataset = new DataSet();
|
DataSet dataset = new DataSet();
|
||||||
dataset.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
dataset.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
||||||
@@ -199,16 +200,16 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
dataset = enumerator.Process(this.sqlConnection, request);
|
dataset = enumerator.Process(this.sqlConnection, request);
|
||||||
|
|
||||||
if (dataset.Tables[0].Rows.Count > 0)
|
if (dataset.Tables[0].Rows.Count > 0)
|
||||||
{
|
{
|
||||||
BackupDeviceType controllerType = (BackupDeviceType)(Convert.ToInt16(dataset.Tables[0].Rows[0]["BackupDeviceType"], System.Globalization.CultureInfo.InvariantCulture));
|
BackupDeviceType controllerType = (BackupDeviceType)(Convert.ToInt16(dataset.Tables[0].Rows[0]["BackupDeviceType"], System.Globalization.CultureInfo.InvariantCulture));
|
||||||
return controllerType;
|
return controllerType;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new Exception("Unexpected error");
|
throw new Exception("Unexpected error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ServerHasTapes()
|
public bool ServerHasTapes()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -221,12 +222,12 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
ds = en.Process(this.sqlConnection, req);
|
ds = en.Process(this.sqlConnection, req);
|
||||||
|
|
||||||
if (ds.Tables[0].Rows.Count > 0)
|
if (ds.Tables[0].Rows.Count > 0)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
catch(Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -241,18 +242,18 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
DataSet ds = new DataSet();
|
DataSet ds = new DataSet();
|
||||||
ds.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
ds.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
||||||
req.Urn = "Server/BackupDevice";
|
req.Urn = "Server/BackupDevice";
|
||||||
ds = en.Process(this.sqlConnection,req);
|
ds = en.Process(this.sqlConnection, req);
|
||||||
|
|
||||||
if (ds.Tables[0].Rows.Count > 0)
|
if (ds.Tables[0].Rows.Count > 0)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
catch(Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -265,8 +266,8 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
char[] result = name.ToCharArray();
|
char[] result = name.ToCharArray();
|
||||||
string illegalCharacters = "\\/:*?\"<>|";
|
string illegalCharacters = "\\/:*?\"<>|";
|
||||||
|
|
||||||
int resultLength = result.GetLength(0);
|
int resultLength = result.GetLength(0);
|
||||||
int illegalLength = illegalCharacters.Length;
|
int illegalLength = illegalCharacters.Length;
|
||||||
|
|
||||||
for (int resultIndex = 0; resultIndex < resultLength; resultIndex++)
|
for (int resultIndex = 0; resultIndex < resultLength; resultIndex++)
|
||||||
{
|
{
|
||||||
@@ -278,17 +279,17 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new string(result);
|
return new string(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RecoveryModel GetRecoveryModel(string databaseName)
|
public RecoveryModel GetRecoveryModel(string databaseName)
|
||||||
{
|
{
|
||||||
Enumerator en = null;
|
Enumerator en = null;
|
||||||
DataSet ds = new DataSet();
|
DataSet ds = new DataSet();
|
||||||
ds.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
ds.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
||||||
Request req = new Request();
|
Request req = new Request();
|
||||||
RecoveryModel recoveryModel = RecoveryModel.Simple;
|
RecoveryModel recoveryModel = RecoveryModel.Simple;
|
||||||
|
|
||||||
en = new Enumerator();
|
en = new Enumerator();
|
||||||
req.Urn = "Server/Database[@Name='" + Urn.EscapeString(databaseName) + "']/Option";
|
req.Urn = "Server/Database[@Name='" + Urn.EscapeString(databaseName) + "']/Option";
|
||||||
@@ -297,9 +298,9 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
ds = en.Process(this.sqlConnection, req);
|
ds = en.Process(this.sqlConnection, req);
|
||||||
|
|
||||||
if (ds.Tables[0].Rows.Count > 0)
|
if (ds.Tables[0].Rows.Count > 0)
|
||||||
{
|
{
|
||||||
recoveryModel = (RecoveryModel)(ds.Tables[0].Rows[0]["RecoveryModel"]);
|
recoveryModel = (RecoveryModel)(ds.Tables[0].Rows[0]["RecoveryModel"]);
|
||||||
}
|
}
|
||||||
return recoveryModel;
|
return recoveryModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,7 +320,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
{
|
{
|
||||||
recoveryModelString = BackupConstants.RecoveryModelBulk;
|
recoveryModelString = BackupConstants.RecoveryModelBulk;
|
||||||
}
|
}
|
||||||
|
|
||||||
return recoveryModelString;
|
return recoveryModelString;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,6 +343,69 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
return backupFolder;
|
return backupFolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetDefaultDataFolder(ServerConnection connection)
|
||||||
|
{
|
||||||
|
string dataFolder = string.Empty;
|
||||||
|
|
||||||
|
Enumerator en = null;
|
||||||
|
DataSet ds = new DataSet();
|
||||||
|
ds.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
||||||
|
Request req = new Request();
|
||||||
|
en = new Enumerator();
|
||||||
|
req.Urn = "Server/Setting";
|
||||||
|
ds = en.Process(connection, req);
|
||||||
|
|
||||||
|
if (ds.Tables[0].Rows.Count > 0)
|
||||||
|
{
|
||||||
|
dataFolder = Convert.ToString(ds.Tables[0].Rows[0]["DefaultFile"], System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
return dataFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string[] GetAssociatedFilePaths(ServerConnection connection, string primaryFilePath)
|
||||||
|
{
|
||||||
|
var databaseFiles = new List<string>();
|
||||||
|
|
||||||
|
var enumerator = new Enumerator();
|
||||||
|
var req = new Request()
|
||||||
|
{
|
||||||
|
Urn = $"Server/PrimaryFile[@Name='{Urn.EscapeString(primaryFilePath)}']/File",
|
||||||
|
Fields = new string[] { "IsFile", "FileName" }
|
||||||
|
};
|
||||||
|
var dataTable = (DataTable)enumerator.Process(connection, req);
|
||||||
|
|
||||||
|
foreach (DataRow currentRow in dataTable.Rows)
|
||||||
|
{
|
||||||
|
var primaryFolder = Path.GetDirectoryName(primaryFilePath);
|
||||||
|
var originalPath = (string)currentRow["FileName"];
|
||||||
|
var originalFileName = Path.GetFileName(originalPath);
|
||||||
|
var filePath = Path.Join(primaryFolder, originalFileName);
|
||||||
|
|
||||||
|
// Check if file exists with the constructed path.
|
||||||
|
// If it's an XI (XStore Integration) path, then assume it exists, otherwise retrieve info for the file to check if it exists.
|
||||||
|
var exists = true;
|
||||||
|
var isXIPath = PathWrapper.IsXIPath(primaryFilePath);
|
||||||
|
if (!isXIPath)
|
||||||
|
{
|
||||||
|
var request = new Request()
|
||||||
|
{
|
||||||
|
Urn = string.Format(System.Globalization.CultureInfo.CurrentCulture, "Server/File[@FullName='{0}']", Urn.EscapeString(filePath)),
|
||||||
|
Fields = new string[] { "IsFile" }
|
||||||
|
};
|
||||||
|
|
||||||
|
DataTable data = (new Enumerator()).Process(connection, request);
|
||||||
|
|
||||||
|
// If the enumerator could find the file, then it exists
|
||||||
|
exists = data?.Rows.Count > 0;
|
||||||
|
}
|
||||||
|
if (exists)
|
||||||
|
{
|
||||||
|
databaseFiles.Add(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return databaseFiles.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
public int GetMediaRetentionValue()
|
public int GetMediaRetentionValue()
|
||||||
{
|
{
|
||||||
int afterDays = 0;
|
int afterDays = 0;
|
||||||
@@ -353,29 +417,29 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
ds.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
ds.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
||||||
|
|
||||||
req.Urn = "Server/Configuration";
|
req.Urn = "Server/Configuration";
|
||||||
ds = en.Process(this.sqlConnection, req);
|
ds = en.Process(this.sqlConnection, req);
|
||||||
for (int i = 0 ; i < ds.Tables[0].Rows.Count; i++)
|
for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
|
||||||
{
|
{
|
||||||
if (Convert.ToString(ds.Tables[0].Rows[i]["Name"], System.Globalization.CultureInfo.InvariantCulture) == "media retention")
|
if (Convert.ToString(ds.Tables[0].Rows[i]["Name"], System.Globalization.CultureInfo.InvariantCulture) == "media retention")
|
||||||
{
|
{
|
||||||
afterDays = Convert.ToInt32(ds.Tables[0].Rows[i]["RunValue"], System.Globalization.CultureInfo.InvariantCulture);
|
afterDays = Convert.ToInt32(ds.Tables[0].Rows[i]["RunValue"], System.Globalization.CultureInfo.InvariantCulture);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return afterDays;
|
return afterDays;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
return afterDays;
|
return afterDays;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetMediaNameFromBackupSetId(int backupSetId)
|
public string GetMediaNameFromBackupSetId(int backupSetId)
|
||||||
{
|
{
|
||||||
Enumerator en = null;
|
Enumerator en = null;
|
||||||
DataSet ds = new DataSet();
|
DataSet ds = new DataSet();
|
||||||
ds.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
ds.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
||||||
Request req = new Request();
|
Request req = new Request();
|
||||||
|
|
||||||
int mediaId = -1;
|
int mediaId = -1;
|
||||||
string mediaName = string.Empty;
|
string mediaName = string.Empty;
|
||||||
@@ -402,7 +466,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
return mediaName;
|
return mediaName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetFileType(string type)
|
public string GetFileType(string type)
|
||||||
@@ -429,8 +493,8 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: This is implemented as internal property in SMO.
|
// TODO: This is implemented as internal property in SMO.
|
||||||
public bool IsLocalPrimaryReplica(string databaseName)
|
public bool IsLocalPrimaryReplica(string databaseName)
|
||||||
@@ -450,7 +514,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
}
|
}
|
||||||
return !string.IsNullOrEmpty(server.Databases[databaseName].AvailabilityGroupName);
|
return !string.IsNullOrEmpty(server.Databases[databaseName].AvailabilityGroupName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns whether mirroring is enabled on a database or not
|
/// Returns whether mirroring is enabled on a database or not
|
||||||
/// </summary>>
|
/// </summary>>
|
||||||
@@ -509,19 +573,19 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsDatabaseOnServer(string databaseName)
|
public bool IsDatabaseOnServer(string databaseName)
|
||||||
{
|
{
|
||||||
Enumerator en = new Enumerator();
|
Enumerator en = new Enumerator();
|
||||||
DataSet ds = new DataSet();
|
DataSet ds = new DataSet();
|
||||||
ds.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
ds.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
||||||
Request req = new Request();
|
Request req = new Request();
|
||||||
|
|
||||||
req.Urn = "Server/Database[@Name='" + Urn.EscapeString(databaseName) + "']";
|
req.Urn = "Server/Database[@Name='" + Urn.EscapeString(databaseName) + "']";
|
||||||
req.Fields = new string[1];
|
req.Fields = new string[1];
|
||||||
req.Fields[0] = "Name";
|
req.Fields[0] = "Name";
|
||||||
|
|
||||||
ds = en.Process(sqlConnection, req);
|
ds = en.Process(sqlConnection, req);
|
||||||
return (ds.Tables[0].Rows.Count > 0) ? true : false;
|
return (ds.Tables[0].Rows.Count > 0) ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -617,7 +681,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
case BackupSetType.Differential:
|
case BackupSetType.Differential:
|
||||||
backupType = RestoreConstants.TypeDifferential;
|
backupType = RestoreConstants.TypeDifferential;
|
||||||
backupComponent = RestoreConstants.ComponentDatabase;
|
backupComponent = RestoreConstants.ComponentDatabase;
|
||||||
break;
|
break;
|
||||||
case BackupSetType.FileOrFileGroup:
|
case BackupSetType.FileOrFileGroup:
|
||||||
backupType = RestoreConstants.TypeFilegroup;
|
backupType = RestoreConstants.TypeFilegroup;
|
||||||
backupComponent = RestoreConstants.ComponentFile;
|
backupComponent = RestoreConstants.ComponentFile;
|
||||||
@@ -636,9 +700,9 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetBackupSetTypeAndComponent(string strType, ref string backupType, ref string backupComponent)
|
public void GetBackupSetTypeAndComponent(string strType, ref string backupType, ref string backupComponent)
|
||||||
{
|
{
|
||||||
string type = strType.ToUpperInvariant();
|
string type = strType.ToUpperInvariant();
|
||||||
|
|
||||||
if (type == "D")
|
if (type == "D")
|
||||||
@@ -682,7 +746,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetFileType(string backupType, string tempFileType, ref string fileType)
|
public void GetFileType(string backupType, string tempFileType, ref string fileType)
|
||||||
@@ -694,20 +758,23 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case "D": fileType = RestoreConstants.Data;
|
case "D":
|
||||||
|
fileType = RestoreConstants.Data;
|
||||||
break;
|
break;
|
||||||
case "S": fileType = RestoreConstants.FileStream;
|
case "S":
|
||||||
|
fileType = RestoreConstants.FileStream;
|
||||||
break;
|
break;
|
||||||
default: fileType = RestoreConstants.NotKnown;
|
default:
|
||||||
|
fileType = RestoreConstants.NotKnown;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public BackupsetType GetBackupsetTypeFromBackupTypesOnDevice(int type)
|
public BackupsetType GetBackupsetTypeFromBackupTypesOnDevice(int type)
|
||||||
{
|
{
|
||||||
BackupsetType Result = BackupsetType.BackupsetDatabase;
|
BackupsetType Result = BackupsetType.BackupsetDatabase;
|
||||||
switch(type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
Result = BackupsetType.BackupsetDatabase;
|
Result = BackupsetType.BackupsetDatabase;
|
||||||
@@ -728,11 +795,11 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public BackupsetType GetBackupsetTypeFromBackupTypesOnHistory(string type)
|
public BackupsetType GetBackupsetTypeFromBackupTypesOnHistory(string type)
|
||||||
{
|
{
|
||||||
BackupsetType result = BackupsetType.BackupsetDatabase;
|
BackupsetType result = BackupsetType.BackupsetDatabase;
|
||||||
switch(type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case "D":
|
case "D":
|
||||||
result = BackupsetType.BackupsetDatabase;
|
result = BackupsetType.BackupsetDatabase;
|
||||||
@@ -752,7 +819,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataSet GetBackupSetFiles(int backupsetId)
|
public DataSet GetBackupSetFiles(int backupsetId)
|
||||||
{
|
{
|
||||||
Enumerator en = new Enumerator();
|
Enumerator en = new Enumerator();
|
||||||
@@ -760,7 +827,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
DataSet backupsetfiles = new DataSet();
|
DataSet backupsetfiles = new DataSet();
|
||||||
backupsetfiles.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
backupsetfiles.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
||||||
|
|
||||||
if(backupsetId > 0)
|
if (backupsetId > 0)
|
||||||
{
|
{
|
||||||
req.Urn = "Server/BackupSet[@ID='" + Urn.EscapeString(Convert.ToString(backupsetId, System.Globalization.CultureInfo.InvariantCulture)) + "']/File";
|
req.Urn = "Server/BackupSet[@ID='" + Urn.EscapeString(Convert.ToString(backupsetId, System.Globalization.CultureInfo.InvariantCulture)) + "']/File";
|
||||||
}
|
}
|
||||||
@@ -772,9 +839,9 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
|
|
||||||
return backupsetfiles;
|
return backupsetfiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataSet GetBackupSetById(int backupsetId)
|
public DataSet GetBackupSetById(int backupsetId)
|
||||||
{
|
{
|
||||||
SqlExecutionModes executionMode = this.sqlConnection.SqlExecutionModes;
|
SqlExecutionModes executionMode = this.sqlConnection.SqlExecutionModes;
|
||||||
this.sqlConnection.SqlExecutionModes = SqlExecutionModes.ExecuteSql;
|
this.sqlConnection.SqlExecutionModes = SqlExecutionModes.ExecuteSql;
|
||||||
Enumerator en = new Enumerator();
|
Enumerator en = new Enumerator();
|
||||||
@@ -788,47 +855,47 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
this.sqlConnection.SqlExecutionModes = executionMode;
|
this.sqlConnection.SqlExecutionModes = executionMode;
|
||||||
return backupset;
|
return backupset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList GetBackupSetPhysicalSources(int backupsetId)
|
public ArrayList GetBackupSetPhysicalSources(int backupsetId)
|
||||||
{
|
{
|
||||||
SqlExecutionModes executionMode = this.sqlConnection.SqlExecutionModes;
|
SqlExecutionModes executionMode = this.sqlConnection.SqlExecutionModes;
|
||||||
this.sqlConnection.SqlExecutionModes = SqlExecutionModes.ExecuteSql;
|
this.sqlConnection.SqlExecutionModes = SqlExecutionModes.ExecuteSql;
|
||||||
|
|
||||||
ArrayList sources = new ArrayList();
|
ArrayList sources = new ArrayList();
|
||||||
DataSet backupSet = GetBackupSetById(backupsetId);
|
DataSet backupSet = GetBackupSetById(backupsetId);
|
||||||
if(backupSet.Tables[0].Rows.Count == 1)
|
if (backupSet.Tables[0].Rows.Count == 1)
|
||||||
{
|
{
|
||||||
string mediaSetID = Convert.ToString(backupSet.Tables[0].Rows[0]["MediaSetId"], System.Globalization.CultureInfo.InvariantCulture);
|
string mediaSetID = Convert.ToString(backupSet.Tables[0].Rows[0]["MediaSetId"], System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
Enumerator en = new Enumerator();
|
Enumerator en = new Enumerator();
|
||||||
Request req = new Request();
|
Request req = new Request();
|
||||||
DataSet mediafamily = new DataSet();
|
DataSet mediafamily = new DataSet();
|
||||||
mediafamily.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
mediafamily.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
||||||
|
|
||||||
req.Urn = "Server/BackupMediaSet[@ID='"+Urn.EscapeString(mediaSetID)+"']/MediaFamily";
|
req.Urn = "Server/BackupMediaSet[@ID='" + Urn.EscapeString(mediaSetID) + "']/MediaFamily";
|
||||||
mediafamily = en.Process(this.sqlConnection, req);
|
mediafamily = en.Process(this.sqlConnection, req);
|
||||||
|
|
||||||
if (mediafamily.Tables[0].Rows.Count > 0)
|
if (mediafamily.Tables[0].Rows.Count > 0)
|
||||||
{
|
{
|
||||||
for (int j = 0 ; j < mediafamily.Tables[0].Rows.Count; j ++)
|
for (int j = 0; j < mediafamily.Tables[0].Rows.Count; j++)
|
||||||
{
|
{
|
||||||
RestoreItemSource itemSource = new RestoreItemSource();
|
RestoreItemSource itemSource = new RestoreItemSource();
|
||||||
itemSource.RestoreItemLocation = Convert.ToString(mediafamily.Tables[0].Rows[j]["PhysicalDeviceName"], System.Globalization.CultureInfo.InvariantCulture);
|
itemSource.RestoreItemLocation = Convert.ToString(mediafamily.Tables[0].Rows[j]["PhysicalDeviceName"], System.Globalization.CultureInfo.InvariantCulture);
|
||||||
BackupDeviceType backupDeviceType = (BackupDeviceType)Enum.Parse(typeof(BackupDeviceType), mediafamily.Tables[0].Rows[j]["BackupDeviceType"].ToString());
|
BackupDeviceType backupDeviceType = (BackupDeviceType)Enum.Parse(typeof(BackupDeviceType), mediafamily.Tables[0].Rows[j]["BackupDeviceType"].ToString());
|
||||||
|
|
||||||
if (BackupDeviceType.Disk == backupDeviceType)
|
if (BackupDeviceType.Disk == backupDeviceType)
|
||||||
{
|
{
|
||||||
itemSource.RestoreItemDeviceType = DeviceType.File;
|
itemSource.RestoreItemDeviceType = DeviceType.File;
|
||||||
}
|
}
|
||||||
else if (BackupDeviceType.Url == backupDeviceType)
|
else if (BackupDeviceType.Url == backupDeviceType)
|
||||||
{
|
{
|
||||||
itemSource.RestoreItemDeviceType = DeviceType.Url;
|
itemSource.RestoreItemDeviceType = DeviceType.Url;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
itemSource.RestoreItemDeviceType = DeviceType.Tape;
|
itemSource.RestoreItemDeviceType = DeviceType.Tape;
|
||||||
}
|
}
|
||||||
sources.Add(itemSource);
|
sources.Add(itemSource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -836,11 +903,11 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
this.sqlConnection.SqlExecutionModes = executionMode;
|
this.sqlConnection.SqlExecutionModes = executionMode;
|
||||||
return sources;
|
return sources;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RestoreActionType GetRestoreTaskFromBackupSetType(BackupsetType type)
|
public RestoreActionType GetRestoreTaskFromBackupSetType(BackupsetType type)
|
||||||
{
|
{
|
||||||
RestoreActionType result = RestoreActionType.Database;
|
RestoreActionType result = RestoreActionType.Database;
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case BackupsetType.BackupsetDatabase:
|
case BackupsetType.BackupsetDatabase:
|
||||||
@@ -861,16 +928,16 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetLatestBackup(string databaseName, string backupSetName)
|
public int GetLatestBackup(string databaseName, string backupSetName)
|
||||||
{
|
{
|
||||||
Enumerator en = new Enumerator();
|
Enumerator en = new Enumerator();
|
||||||
Request req = new Request();
|
Request req = new Request();
|
||||||
DataSet backupSets = new DataSet();
|
DataSet backupSets = new DataSet();
|
||||||
backupSets.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
backupSets.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
||||||
OrderBy orderByBackupDate;
|
OrderBy orderByBackupDate;
|
||||||
|
|
||||||
req.Urn = "Server/BackupSet[@Name='"+Urn.EscapeString(backupSetName)+"' and @DatabaseName='"+ Urn.EscapeString(databaseName)+"']";
|
req.Urn = "Server/BackupSet[@Name='" + Urn.EscapeString(backupSetName) + "' and @DatabaseName='" + Urn.EscapeString(databaseName) + "']";
|
||||||
req.OrderByList = new OrderBy[1];
|
req.OrderByList = new OrderBy[1];
|
||||||
orderByBackupDate = new OrderBy("BackupFinishDate", OrderBy.Direction.Desc);
|
orderByBackupDate = new OrderBy("BackupFinishDate", OrderBy.Direction.Desc);
|
||||||
req.OrderByList[0] = orderByBackupDate;
|
req.OrderByList[0] = orderByBackupDate;
|
||||||
@@ -885,7 +952,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<RestoreItemSource> GetLatestBackupLocations(string databaseName)
|
public List<RestoreItemSource> GetLatestBackupLocations(string databaseName)
|
||||||
{
|
{
|
||||||
List<RestoreItemSource> latestLocations = new List<RestoreItemSource>();
|
List<RestoreItemSource> latestLocations = new List<RestoreItemSource>();
|
||||||
@@ -944,20 +1011,20 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
}
|
}
|
||||||
/// LPU doesn't have rights to enumerate msdb.backupset
|
/// LPU doesn't have rights to enumerate msdb.backupset
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
return latestLocations;
|
return latestLocations;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetDefaultDatabaseForLogin(string loginName)
|
public string GetDefaultDatabaseForLogin(string loginName)
|
||||||
{
|
{
|
||||||
string defaultDatabase = string.Empty;
|
string defaultDatabase = string.Empty;
|
||||||
Enumerator en = new Enumerator();
|
Enumerator en = new Enumerator();
|
||||||
DataSet ds = new DataSet();
|
DataSet ds = new DataSet();
|
||||||
ds.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
ds.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
||||||
Request req = new Request();
|
Request req = new Request();
|
||||||
|
|
||||||
req.Urn = "Server/Login[@Name='"+Urn.EscapeString(loginName)+"']";
|
req.Urn = "Server/Login[@Name='" + Urn.EscapeString(loginName) + "']";
|
||||||
req.Fields = new string[1];
|
req.Fields = new string[1];
|
||||||
req.Fields[0] = "DefaultDatabase";
|
req.Fields[0] = "DefaultDatabase";
|
||||||
ds = en.Process(this.sqlConnection, req);
|
ds = en.Process(this.sqlConnection, req);
|
||||||
@@ -991,26 +1058,26 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList IsPhysicalPathInLogicalDevice(string physicalPath)
|
public ArrayList IsPhysicalPathInLogicalDevice(string physicalPath)
|
||||||
{
|
{
|
||||||
Enumerator en = new Enumerator();
|
Enumerator en = new Enumerator();
|
||||||
DataSet ds = new DataSet();
|
DataSet ds = new DataSet();
|
||||||
ds.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
ds.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
||||||
Request req = new Request();
|
Request req = new Request();
|
||||||
ArrayList result = null;
|
ArrayList result = null;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
req.Urn = "Server/BackupDevice[@PhysicalLocation='" +Urn.EscapeString(physicalPath)+ "']";
|
req.Urn = "Server/BackupDevice[@PhysicalLocation='" + Urn.EscapeString(physicalPath) + "']";
|
||||||
|
|
||||||
ds = en.Process(this.sqlConnection, req);
|
ds = en.Process(this.sqlConnection, req);
|
||||||
count = ds.Tables[0].Rows.Count;
|
count = ds.Tables[0].Rows.Count;
|
||||||
|
|
||||||
if (count > 0)
|
if (count > 0)
|
||||||
{
|
{
|
||||||
result = new ArrayList(count);
|
result = new ArrayList(count);
|
||||||
for(int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
result.Add(Convert.ToString(ds.Tables[0].Rows[0]["Name"], System.Globalization.CultureInfo.InvariantCulture));
|
result.Add(Convert.ToString(ds.Tables[0].Rows[0]["Name"], System.Globalization.CultureInfo.InvariantCulture));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1025,7 +1092,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
return System.Environment.MachineName;
|
return System.Environment.MachineName;
|
||||||
}
|
}
|
||||||
|
|
||||||
string machineName = sqlServerName;
|
string machineName = sqlServerName;
|
||||||
if (sqlServerName.Trim().Length != 0)
|
if (sqlServerName.Trim().Length != 0)
|
||||||
{
|
{
|
||||||
// [0] = machine, [1] = instance
|
// [0] = machine, [1] = instance
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts
|
||||||
|
{
|
||||||
|
public class DatabaseFileData
|
||||||
|
{
|
||||||
|
public string DatabaseName { get; set; }
|
||||||
|
public string[] DatabaseFilePaths { get; set; }
|
||||||
|
public string Owner { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AttachDatabaseRequestParams
|
||||||
|
{
|
||||||
|
public string ConnectionUri { get; set; }
|
||||||
|
public DatabaseFileData[] Databases { get; set; }
|
||||||
|
public bool GenerateScript { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AttachDatabaseRequest
|
||||||
|
{
|
||||||
|
public static readonly RequestType<AttachDatabaseRequestParams, string> Type = RequestType<AttachDatabaseRequestParams, string>.Create("objectManagement/attachDatabase");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -70,6 +70,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
|||||||
this.serviceHost.SetRequestHandler(DisposeViewRequest.Type, HandleDisposeViewRequest, true);
|
this.serviceHost.SetRequestHandler(DisposeViewRequest.Type, HandleDisposeViewRequest, true);
|
||||||
this.serviceHost.SetRequestHandler(SearchRequest.Type, HandleSearchRequest, true);
|
this.serviceHost.SetRequestHandler(SearchRequest.Type, HandleSearchRequest, true);
|
||||||
this.serviceHost.SetRequestHandler(DetachDatabaseRequest.Type, HandleDetachDatabaseRequest, true);
|
this.serviceHost.SetRequestHandler(DetachDatabaseRequest.Type, HandleDetachDatabaseRequest, true);
|
||||||
|
this.serviceHost.SetRequestHandler(AttachDatabaseRequest.Type, HandleAttachDatabaseRequest, true);
|
||||||
this.serviceHost.SetRequestHandler(DropDatabaseRequest.Type, HandleDropDatabaseRequest, true);
|
this.serviceHost.SetRequestHandler(DropDatabaseRequest.Type, HandleDropDatabaseRequest, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +156,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
|||||||
}
|
}
|
||||||
|
|
||||||
SearchableObjectTypeDescription desc = SearchableObjectTypeDescription.GetDescription(searchableObjectType);
|
SearchableObjectTypeDescription desc = SearchableObjectTypeDescription.GetDescription(searchableObjectType);
|
||||||
|
|
||||||
if (desc.IsDatabaseObject)
|
if (desc.IsDatabaseObject)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(requestParams.Schema))
|
if (!string.IsNullOrEmpty(requestParams.Schema))
|
||||||
@@ -207,6 +208,13 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
|||||||
await requestContext.SendResult(sqlScript);
|
await requestContext.SendResult(sqlScript);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal async Task HandleAttachDatabaseRequest(AttachDatabaseRequestParams requestParams, RequestContext<string> requestContext)
|
||||||
|
{
|
||||||
|
var handler = this.GetObjectTypeHandler(SqlObjectType.Database) as DatabaseHandler;
|
||||||
|
var sqlScript = handler.Attach(requestParams);
|
||||||
|
await requestContext.SendResult(sqlScript);
|
||||||
|
}
|
||||||
|
|
||||||
internal async Task HandleDropDatabaseRequest(DropDatabaseRequestParams requestParams, RequestContext<string> requestContext)
|
internal async Task HandleDropDatabaseRequest(DropDatabaseRequestParams requestParams, RequestContext<string> requestContext)
|
||||||
{
|
{
|
||||||
var handler = this.GetObjectTypeHandler(SqlObjectType.Database) as DatabaseHandler;
|
var handler = this.GetObjectTypeHandler(SqlObjectType.Database) as DatabaseHandler;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ using Microsoft.SqlTools.Utility;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Utility.SqlScriptFormatters;
|
using Microsoft.SqlTools.ServiceLayer.Utility.SqlScriptFormatters;
|
||||||
|
using System.Collections.Specialized;
|
||||||
using Microsoft.SqlTools.SqlCore.Utility;
|
using Microsoft.SqlTools.SqlCore.Utility;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||||
@@ -393,6 +394,60 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
|||||||
return builder.ToString();
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Attach(AttachDatabaseRequestParams attachParams)
|
||||||
|
{
|
||||||
|
var sqlScript = string.Empty;
|
||||||
|
ConnectionInfo connectionInfo = this.GetConnectionInfo(attachParams.ConnectionUri);
|
||||||
|
using (var dataContainer = CreateDatabaseDataContainer(attachParams.ConnectionUri, null, true, null))
|
||||||
|
{
|
||||||
|
var server = dataContainer.Server!;
|
||||||
|
var originalExecuteMode = server.ConnectionContext.SqlExecutionModes;
|
||||||
|
if (attachParams.GenerateScript)
|
||||||
|
{
|
||||||
|
server.ConnectionContext.SqlExecutionModes = SqlExecutionModes.CaptureSql;
|
||||||
|
server.ConnectionContext.CapturedSql.Clear();
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var database in attachParams.Databases)
|
||||||
|
{
|
||||||
|
var fileCollection = new StringCollection();
|
||||||
|
fileCollection.AddRange(database.DatabaseFilePaths);
|
||||||
|
if (database.Owner != SR.general_default)
|
||||||
|
{
|
||||||
|
server.AttachDatabase(database.DatabaseName, fileCollection, database.Owner);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
server.AttachDatabase(database.DatabaseName, fileCollection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (attachParams.GenerateScript)
|
||||||
|
{
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
var capturedText = server.ConnectionContext.CapturedSql.Text;
|
||||||
|
foreach (var entry in capturedText)
|
||||||
|
{
|
||||||
|
if (entry != null)
|
||||||
|
{
|
||||||
|
builder.AppendLine(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqlScript = builder.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (attachParams.GenerateScript)
|
||||||
|
{
|
||||||
|
server.ConnectionContext.SqlExecutionModes = originalExecuteMode;
|
||||||
|
}
|
||||||
|
dataContainer.ServerConnection.Disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sqlScript;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to drop the specified database
|
/// Used to drop the specified database
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -3,9 +3,12 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Data.SqlClient;
|
using Microsoft.Data.SqlClient;
|
||||||
using Microsoft.SqlServer.Management.Common;
|
using Microsoft.SqlServer.Management.Common;
|
||||||
@@ -356,7 +359,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
|
|||||||
testDatabase.DatabaseScopedConfigurations[0].ValueForSecondary = "OFF";
|
testDatabase.DatabaseScopedConfigurations[0].ValueForSecondary = "OFF";
|
||||||
}
|
}
|
||||||
await ObjectManagementTestUtils.SaveObject(parametersForUpdate, testDatabase);
|
await ObjectManagementTestUtils.SaveObject(parametersForUpdate, testDatabase);
|
||||||
DatabaseViewInfo updatedDatabaseViewInfo = await ObjectManagementTestUtils.GetDatabaseObject(parametersForUpdate, testDatabase);
|
DatabaseViewInfo updatedDatabaseViewInfo = await ObjectManagementTestUtils.GetDatabaseObject(parametersForUpdate, testDatabase);
|
||||||
|
|
||||||
// verify the modified properties
|
// verify the modified properties
|
||||||
Assert.That(((DatabaseInfo)updatedDatabaseViewInfo.ObjectInfo).DatabaseScopedConfigurations[0].ValueForPrimary, Is.EqualTo(testDatabase.DatabaseScopedConfigurations[0].ValueForPrimary), $"DSC updated primary value should match");
|
Assert.That(((DatabaseInfo)updatedDatabaseViewInfo.ObjectInfo).DatabaseScopedConfigurations[0].ValueForPrimary, Is.EqualTo(testDatabase.DatabaseScopedConfigurations[0].ValueForPrimary), $"DSC updated primary value should match");
|
||||||
@@ -500,6 +503,107 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[TestCase(true)]
|
||||||
|
[TestCase(false)]
|
||||||
|
public async Task AttachDatabaseTest(bool generateScript)
|
||||||
|
{
|
||||||
|
using (SqlTestDb testDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, null, nameof(AttachDatabaseTest)))
|
||||||
|
{
|
||||||
|
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", serverType: TestServerType.OnPrem);
|
||||||
|
using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connectionResult.ConnectionInfo))
|
||||||
|
{
|
||||||
|
var serverConn = new ServerConnection(sqlConn);
|
||||||
|
var server = new Server(serverConn);
|
||||||
|
var objUrn = ObjectManagementTestUtils.GetDatabaseURN(testDb.DatabaseName);
|
||||||
|
var database = server.GetSmoObject(objUrn) as Database;
|
||||||
|
|
||||||
|
var originalOwner = database!.Owner;
|
||||||
|
var originalFilePaths = new List<string>();
|
||||||
|
foreach (FileGroup group in database.FileGroups)
|
||||||
|
{
|
||||||
|
foreach (DataFile file in group.Files)
|
||||||
|
{
|
||||||
|
originalFilePaths.Add(file.FileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (LogFile file in database.LogFiles)
|
||||||
|
{
|
||||||
|
originalFilePaths.Add(file.FileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detach database so that we can re-attach it with the database handler method.
|
||||||
|
// Have to set database to single user mode to close active connections before detaching it.
|
||||||
|
database.DatabaseOptions.UserAccess = SqlServer.Management.Smo.DatabaseUserAccess.Single;
|
||||||
|
database.Alter(TerminationClause.RollbackTransactionsImmediately);
|
||||||
|
server.DetachDatabase(testDb.DatabaseName, false);
|
||||||
|
var dbExists = this.DatabaseExists(testDb.DatabaseName, server);
|
||||||
|
Assert.That(dbExists, Is.False, "Database was not correctly detached before doing attach test.");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var handler = new DatabaseHandler(ConnectionService.Instance);
|
||||||
|
var attachParams = new AttachDatabaseRequestParams()
|
||||||
|
{
|
||||||
|
ConnectionUri = connectionResult.ConnectionInfo.OwnerUri,
|
||||||
|
Databases = new DatabaseFileData[]
|
||||||
|
{
|
||||||
|
new DatabaseFileData()
|
||||||
|
{
|
||||||
|
Owner = originalOwner,
|
||||||
|
DatabaseName = testDb.DatabaseName,
|
||||||
|
DatabaseFilePaths = originalFilePaths.ToArray()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
GenerateScript = generateScript
|
||||||
|
};
|
||||||
|
var script = handler.Attach(attachParams);
|
||||||
|
|
||||||
|
if (generateScript)
|
||||||
|
{
|
||||||
|
dbExists = this.DatabaseExists(testDb.DatabaseName, server);
|
||||||
|
Assert.That(dbExists, Is.False, "Should not have attached DB when only generating a script.");
|
||||||
|
|
||||||
|
var queryBuilder = new StringBuilder();
|
||||||
|
queryBuilder.AppendLine("USE [master]");
|
||||||
|
queryBuilder.AppendLine($"CREATE DATABASE [{testDb.DatabaseName}] ON ");
|
||||||
|
|
||||||
|
for (int i = 0; i < originalFilePaths.Count - 1; i++)
|
||||||
|
{
|
||||||
|
var file = originalFilePaths[i];
|
||||||
|
queryBuilder.AppendLine($"( FILENAME = N'{file}' ),");
|
||||||
|
}
|
||||||
|
queryBuilder.AppendLine($"( FILENAME = N'{originalFilePaths[originalFilePaths.Count - 1]}' )");
|
||||||
|
|
||||||
|
queryBuilder.AppendLine(" FOR ATTACH");
|
||||||
|
queryBuilder.AppendLine($"if exists (select name from master.sys.databases sd where name = N'{testDb.DatabaseName}' and SUSER_SNAME(sd.owner_sid) = SUSER_SNAME() ) EXEC [{testDb.DatabaseName}].dbo.sp_changedbowner @loginame=N'{originalOwner}', @map=false");
|
||||||
|
|
||||||
|
Assert.That(script, Is.EqualTo(queryBuilder.ToString()), "Did not get expected attach database script");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.That(script, Is.Empty, "Should not have generated a script for this Attach operation.");
|
||||||
|
|
||||||
|
server.Databases.Refresh();
|
||||||
|
dbExists = this.DatabaseExists(testDb.DatabaseName, server);
|
||||||
|
Assert.That(dbExists, "Database was not attached successfully");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
dbExists = this.DatabaseExists(testDb.DatabaseName, server);
|
||||||
|
if (!dbExists)
|
||||||
|
{
|
||||||
|
// Reattach database so it can get dropped during cleanup
|
||||||
|
var fileCollection = new StringCollection();
|
||||||
|
originalFilePaths.ForEach(file => fileCollection.Add(file));
|
||||||
|
server.AttachDatabase(testDb.DatabaseName, fileCollection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task DeleteDatabaseTest()
|
public async Task DeleteDatabaseTest()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
using Microsoft.SqlServer.Management.Common;
|
||||||
|
using Microsoft.SqlServer.Management.Smo;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Test.Common;
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
|
||||||
|
{
|
||||||
|
public class UtilsTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public async Task GetDataFolderTest()
|
||||||
|
{
|
||||||
|
using (SqlTestDb testDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, null, nameof(GetDataFolderTest)))
|
||||||
|
{
|
||||||
|
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync(testDb.DatabaseName, serverType: TestServerType.OnPrem);
|
||||||
|
using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connectionResult.ConnectionInfo))
|
||||||
|
{
|
||||||
|
var serverConn = new ServerConnection(sqlConn);
|
||||||
|
var server = new Server(serverConn);
|
||||||
|
var objUrn = ObjectManagementTestUtils.GetDatabaseURN(testDb.DatabaseName);
|
||||||
|
var database = server.GetSmoObject(objUrn) as Database;
|
||||||
|
|
||||||
|
var dataFilePath = database.FileGroups[0].Files[0].FileName;
|
||||||
|
var expectedDataFolder = Path.GetDirectoryName(dataFilePath).ToString();
|
||||||
|
|
||||||
|
var actualDataFolder = CommonUtilities.GetDefaultDataFolder(serverConn);
|
||||||
|
actualDataFolder = Path.TrimEndingDirectorySeparator(actualDataFolder);
|
||||||
|
Assert.That(actualDataFolder, Is.EqualTo(expectedDataFolder).IgnoreCase, "Did not get expected data file folder path.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task GetAssociatedFilesTest()
|
||||||
|
{
|
||||||
|
using (SqlTestDb testDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, null, nameof(GetAssociatedFilesTest)))
|
||||||
|
{
|
||||||
|
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync(testDb.DatabaseName, serverType: TestServerType.OnPrem);
|
||||||
|
using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connectionResult.ConnectionInfo))
|
||||||
|
{
|
||||||
|
var serverConn = new ServerConnection(sqlConn);
|
||||||
|
var server = new Server(serverConn);
|
||||||
|
var objUrn = ObjectManagementTestUtils.GetDatabaseURN(testDb.DatabaseName);
|
||||||
|
var database = server.GetSmoObject(objUrn) as Database;
|
||||||
|
|
||||||
|
var expectedFilePaths = new List<string>();
|
||||||
|
DataFile primaryFile = null;
|
||||||
|
foreach (FileGroup group in database.FileGroups)
|
||||||
|
{
|
||||||
|
foreach (DataFile file in group.Files)
|
||||||
|
{
|
||||||
|
expectedFilePaths.Add(file.FileName);
|
||||||
|
if (file.IsPrimaryFile)
|
||||||
|
{
|
||||||
|
primaryFile = file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (LogFile file in database.LogFiles)
|
||||||
|
{
|
||||||
|
expectedFilePaths.Add(file.FileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detach database so that we don't throw an error when trying to access the primary data file
|
||||||
|
// Have to set database to single user mode to close active connections before detaching it.
|
||||||
|
database.DatabaseOptions.UserAccess = SqlServer.Management.Smo.DatabaseUserAccess.Single;
|
||||||
|
database.Alter(TerminationClause.RollbackTransactionsImmediately);
|
||||||
|
server.DetachDatabase(testDb.DatabaseName, false);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Assert.That(primaryFile, Is.Not.Null, "Could not find a primary file in the list of database files.");
|
||||||
|
var actualFilePaths = CommonUtilities.GetAssociatedFilePaths(serverConn, primaryFile.FileName);
|
||||||
|
Assert.That(actualFilePaths, Is.EqualTo(expectedFilePaths).IgnoreCase, "The list of associated files did not match the actual files for the database.");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Reattach database so it can get dropped during cleanup
|
||||||
|
var fileCollection = new StringCollection();
|
||||||
|
expectedFilePaths.ForEach(file => fileCollection.Add(file));
|
||||||
|
server.AttachDatabase(testDb.DatabaseName, fileCollection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task ThrowErrorWhenDatabaseExistsTest()
|
||||||
|
{
|
||||||
|
using (SqlTestDb testDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, null, nameof(ThrowErrorWhenDatabaseExistsTest)))
|
||||||
|
{
|
||||||
|
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync(testDb.DatabaseName, serverType: TestServerType.OnPrem);
|
||||||
|
using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connectionResult.ConnectionInfo))
|
||||||
|
{
|
||||||
|
var serverConn = new ServerConnection(sqlConn);
|
||||||
|
var server = new Server(serverConn);
|
||||||
|
var objUrn = ObjectManagementTestUtils.GetDatabaseURN(testDb.DatabaseName);
|
||||||
|
var database = server.GetSmoObject(objUrn) as Database;
|
||||||
|
|
||||||
|
DataFile primaryFile = null;
|
||||||
|
foreach (FileGroup group in database.FileGroups)
|
||||||
|
{
|
||||||
|
foreach (DataFile file in group.Files)
|
||||||
|
{
|
||||||
|
if (file.IsPrimaryFile)
|
||||||
|
{
|
||||||
|
primaryFile = file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.That(
|
||||||
|
() => CommonUtilities.GetAssociatedFilePaths(serverConn, primaryFile.FileName),
|
||||||
|
Throws.Exception,
|
||||||
|
"Should throw an error when trying to open a database file that's already in use."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user