From efe48d8bd1b56aa1650db4f6eb9a9a4b6eb802d1 Mon Sep 17 00:00:00 2001 From: Kate Shin Date: Fri, 12 May 2017 21:55:16 -0700 Subject: [PATCH] Backup in-progress work (#347) * Initial checkin of backup service implementation * Backup initial changes for the quick scenario * misc changes * Misc changes for backup in-progress work --- .../DisasterRecovery/BackupFactory.cs | 360 +++ .../DisasterRecovery/BackupRestoreUtil.cs | 1167 +++++++++ .../DisasterRecovery/Common/DataContainer.cs | 2203 +++++++++++++++++ .../Common/IManagedConnection.cs | 198 ++ .../DisasterRecovery/Common/ISandboxLoader.cs | 31 + .../DisasterRecovery/Common/STParameters.cs | 488 ++++ .../Common/SharedConnectionUtil.cs | 108 + .../DisasterRecovery/Contracts/BackupInfo.cs | 49 +- .../DisasterRecoveryConstants.cs | 47 + .../DisasterRecoveryService.cs | 113 +- .../DisasterRecovery/UrlControl.cs | 34 + .../project.json | 2 +- 12 files changed, 4796 insertions(+), 4 deletions(-) create mode 100644 src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/BackupFactory.cs create mode 100644 src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/BackupRestoreUtil.cs create mode 100644 src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Common/DataContainer.cs create mode 100644 src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Common/IManagedConnection.cs create mode 100644 src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Common/ISandboxLoader.cs create mode 100644 src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Common/STParameters.cs create mode 100644 src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Common/SharedConnectionUtil.cs create mode 100644 src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/DisasterRecoveryConstants.cs create mode 100644 src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/UrlControl.cs diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/BackupFactory.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/BackupFactory.cs new file mode 100644 index 00000000..620002fa --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/BackupFactory.cs @@ -0,0 +1,360 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; +using System.Collections; +using System.ComponentModel; +using System.Data.SqlClient; +using Microsoft.SqlServer.Management.Sdk.Sfc; +using Microsoft.SqlServer.Management.Smo; +using Microsoft.SqlServer.Management.Common; +using Microsoft.SqlTools.ServiceLayer.Common; +using Microsoft.Data.Tools.DataSets; +using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts; + +namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery +{ + public class BackupFactory + { + private CDataContainer dataContainer; + private ServerConnection serverConnection; + private BackupRestoreUtil backupRestoreUtil = null; + private UrlControl urlControl; + + /// + /// Constants + /// + private const int constDeviceTypeFile = 2; + private const int constDeviceTypeTape = 5; + private const int constDeviceTypeMediaSet = 3; + + /// + /// UI input values + /// + private BackupInfo backupInfo; + public BackupComponent backupComponent { get; set; } + public BackupType backupType { get; set; } // 0 for Full, 1 for Differential, 2 for Log + public BackupDeviceType backupDeviceType { get; set; } + + private BackupActionType backupActionType = BackupActionType.Database; + private bool IsBackupIncremental = false; + private bool isLocalPrimaryReplica; + + /// this is used when the backup dialog is launched in the context of a backup device + /// The InitialBackupDestination will be loaded in LoadData + private string initialBackupDestination = string.Empty; + + // Helps in populating the properties of an Azure blob given its URI + private class BlobProperties + { + private string containerName; + + public string ContainerName + { + get { return this.containerName; } + } + + private string fileName; + + public string FileName + { + get { return this.fileName; } + } + + public BlobProperties(Uri blobUri) + { + // Extracts the container name and the filename from URI of the strict format https://// + // The input URI should be well formed (Current used context - URI is read from msdb - well formed) + + this.containerName = string.Empty; + this.fileName = string.Empty; + if (blobUri == null) + { + return; + } + string[] seg = blobUri.Segments; + if (seg.Length >= 2) + { + this.containerName = seg[1].Replace("/", ""); + } + if (seg.Length >= 3) + { + this.fileName = seg[2].Replace("/", ""); + } + } + }; + + #region ctors + + /// + /// Ctor + /// + public BackupFactory() + { + } + + /// + /// Initialize variables + /// + /// + /// + /// + public void Initialize(CDataContainer dataContainer, SqlConnection sqlConnection, BackupInfo input) + { + this.dataContainer = dataContainer; + this.serverConnection = new ServerConnection(sqlConnection); // @@ check the value! + this.backupRestoreUtil = new BackupRestoreUtil(this.dataContainer, this.serverConnection); + //this.urlControl.SqlServer = dataContainer.Server; + this.backupInfo = input; + + // convert the types + this.backupComponent = (BackupComponent)input.BackupComponent; + this.backupType = (BackupType)input.BackupType; + this.backupDeviceType = (BackupDeviceType)input.BackupDeviceType; + + if (this.backupRestoreUtil.IsHADRDatabase(this.backupInfo.DatabaseName)) + { + this.isLocalPrimaryReplica = this.backupRestoreUtil.IsLocalPrimaryReplica(this.backupInfo.DatabaseName); + } + + //TODO: when is backup device not null? + //bStatus = param.GetParam("backupdevice", ref this.initialBackupDestination); + } + + #endregion + + #region Methods for UI logic + + // Return recovery model of the current database + private string GetRecoveryModel() + { + RecoveryModel recoveryModel = this.backupRestoreUtil.GetRecoveryModel(this.backupInfo.DatabaseName); + return recoveryModel.ToString(); + } + + /// + /// Return true if backup to URL is supported in the current SQL Server version + /// + private bool BackupToUrlSupported() + { + return BackupRestoreBase.IsBackupUrlDeviceSupported(this.dataContainer.Server.PingSqlServerVersion(this.dataContainer.ServerName)); //@@ originally, DataContainer.Server.ServerVersion + } + + #endregion + + private string GetDefaultBackupSetName() + { + string bkpsetName = this.backupInfo.DatabaseName + "-" + + this.backupType.ToString() + " " + + this.backupComponent.ToString() + " " + + BackupConstants.Backup; + return bkpsetName; + } + + private void SetBackupProps() + { + try + { + switch (this.backupType) + { + case BackupType.Full: + if (this.backupComponent == BackupComponent.Database) // define the value as const!! + { + this.backupActionType = BackupActionType.Database; + } + else if ((this.backupComponent == BackupComponent.Files) && (null != this.backupInfo.SelectedFileGroup) && (this.backupInfo.SelectedFileGroup.Count > 0)) + { + this.backupActionType = BackupActionType.Files; + } + this.IsBackupIncremental = false; + break; + case BackupType.Differential: + if ((this.backupComponent == BackupComponent.Files) && (0 != this.backupInfo.SelectedFiles.Length)) + { + this.backupActionType = BackupActionType.Files; + this.IsBackupIncremental = true; + } + else + { + this.backupActionType = BackupActionType.Database; + this.IsBackupIncremental = true; + } + break; + case BackupType.TransactionLog: + this.backupActionType = BackupActionType.Log; + this.IsBackupIncremental = false; + break; + default: + break; + //throw new Exception("Unexpected error"); + } + } + catch + { + } + } + + /// + /// Sets the backup properties from the general tab + /// + public void PerformBackup() + { + // Set backup action + this.SetBackupProps(); + Backup bk = new Backup(); + try + { + bk.Database = this.backupInfo.DatabaseName; + bk.Action = this.backupActionType; + bk.Incremental = this.IsBackupIncremental; + if (bk.Action == BackupActionType.Files) + { + IDictionaryEnumerator IEnum = this.backupInfo.SelectedFileGroup.GetEnumerator(); + IEnum.Reset(); + while (IEnum.MoveNext()) + { + string CurrentKey = Convert.ToString(IEnum.Key, + System.Globalization.CultureInfo.InvariantCulture); + string CurrentValue = Convert.ToString(IEnum.Value, + System.Globalization.CultureInfo.InvariantCulture); + if (CurrentKey.IndexOf(",", StringComparison.Ordinal) < 0) + { + // is a file group + bk.DatabaseFileGroups.Add(CurrentValue); + } + else + { + // is a file + int Idx = CurrentValue.IndexOf(".", StringComparison.Ordinal); + CurrentValue = CurrentValue.Substring(Idx + 1, CurrentValue.Length - Idx - 1); + bk.DatabaseFiles.Add(CurrentValue); + } + } + } + + bool bBackupToUrl = false; + if (this.backupDeviceType == BackupDeviceType.Url) + { + bBackupToUrl = true; + } + + bk.BackupSetName = this.backupInfo.BackupsetName; + + if (false == bBackupToUrl) + { + for (int i = 0; i < this.backupInfo.BackupPathList.Count; i++) + { + string DestName = Convert.ToString(this.backupInfo.BackupPathList[i], System.Globalization.CultureInfo.InvariantCulture); + int deviceType = (int)(this.backupInfo.arChangesList[DestName]); + switch (deviceType) + { + case (int)DeviceType.LogicalDevice: + int backupDeviceType = + GetDeviceType(Convert.ToString(DestName, + System.Globalization.CultureInfo.InvariantCulture)); + + if ((this.backupDeviceType == BackupDeviceType.Disk && backupDeviceType == constDeviceTypeFile) + || (this.backupDeviceType == BackupDeviceType.Tape && backupDeviceType == constDeviceTypeTape)) + { + bk.Devices.AddDevice(DestName, DeviceType.LogicalDevice); + } + break; + case (int)DeviceType.File: + if (this.backupDeviceType == BackupDeviceType.Disk) + { + bk.Devices.AddDevice(DestName, DeviceType.File); + } + break; + case (int)DeviceType.Tape: + if (this.backupDeviceType == BackupDeviceType.Tape) + { + bk.Devices.AddDevice(DestName, DeviceType.Tape); + } + break; + } + } + } + else + { + /*if (this.urlControl.ListBakDestUrls.Count > 0) + { + // Append the URL filename to the URL prefix + foreach (string urlPath in this.urlControl.ListBakDestUrls.ToArray()) + { + if (!String.IsNullOrWhiteSpace(urlPath)) + { + bk.Devices.AddDevice(urlPath, DeviceType.Url); + } + } + }*/ + } + /* + if (this.dataContainer.HashTable.ContainsKey(bk.BackupSetName)) + { + this.dataContainer.HashTable.Remove(bk.BackupSetName); + } + this.dataContainer.HashTable.Add(bk.BackupSetName, bk);*/ + + //TODO: This should be changed to get user inputs + bk.FormatMedia = false; + bk.Initialize = false; + bk.SkipTapeHeader = true; + bk.Checksum = false; + bk.ContinueAfterError = false; + bk.LogTruncation = BackupTruncateLogType.Truncate; + + // Execute backup + bk.SqlBackup(this.dataContainer.Server); + } + catch + { + } + } + + + private ArrayList getBackupDestinationList() + { + //TODO: return the latest backup destination paths to show to UI dialog + return null; + } + + private int GetDeviceType(string deviceName) + { + Enumerator en = new Enumerator(); + Request req = new Request(); + DataSet ds = new DataSet(); + ds.Locale = System.Globalization.CultureInfo.InvariantCulture; + int Result = -1; + SqlExecutionModes execMode = this.serverConnection.SqlExecutionModes; + this.serverConnection.SqlExecutionModes = SqlExecutionModes.ExecuteSql; + try + { + req.Urn = "Server/BackupDevice[@Name='" + Urn.EscapeString(deviceName) + "']"; + req.Fields = new string[1]; + req.Fields[0] = "BackupDeviceType"; + ds = en.Process(this.serverConnection, req); + int iCount = ds.Tables[0].Rows.Count; + if (iCount > 0) + { + Result = Convert.ToInt16(ds.Tables[0].Rows[0]["BackupDeviceType"], + System.Globalization.CultureInfo.InvariantCulture); + return Result; + } + else + { + return constDeviceTypeMediaSet; + } + } + catch + { + } + finally + { + this.serverConnection.SqlExecutionModes = execMode; + } + return Result; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/BackupRestoreUtil.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/BackupRestoreUtil.cs new file mode 100644 index 00000000..a261eee0 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/BackupRestoreUtil.cs @@ -0,0 +1,1167 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + + +using System; +using System.Collections; +using Microsoft.Data.Tools.DataSets; +using Microsoft.SqlServer.Management.Common; +using Microsoft.SqlServer.Management.Smo; +using Microsoft.SqlServer.Management.Sdk.Sfc; +using SMO = Microsoft.SqlServer.Management.Smo; +using Microsoft.SqlTools.ServiceLayer.Common; + +namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery +{ + public enum BackupType + { + Full, + Differential, + TransactionLog + } + + public enum BackupComponent + { + Database, + Files + } + + + public enum BackupsetType + { + BackupsetDatabase, + BackupsetLog, + BackupsetDifferential, + BackupsetFiles + } + + public enum RecoveryOption + { + Recovery, + NoRecovery, + StandBy + } + + public class RestoreItemSource + { + public string RestoreItemLocation; + public DeviceType RestoreItemDeviceType; + public bool IsLogicalDevice; + + public override int GetHashCode() + { + if (string.IsNullOrEmpty(RestoreItemLocation)) + { + return (RestoreItemDeviceType.ToString() + IsLogicalDevice.ToString()).GetHashCode(); + } + else + { + return (RestoreItemLocation+RestoreItemDeviceType.ToString() + IsLogicalDevice.ToString()).GetHashCode(); + } + } + } + + + public class RestoreItem + { + public BackupsetType ItemBackupsetType; + public int ItemPosition; + public ArrayList ItemSources; + public string ItemName; + public string ItemDescription; + public string ItemMediaName; + } + + /// + /// Summary description for SqlBackupRestoreBase. + /// + public class BackupRestoreUtil + { + private CDataContainer DataContainer; + private ServerConnection SqlConnection = null; + private ArrayList ExcludedDbs; + + public BackupRestoreUtil(CDataContainer dataContainer, ServerConnection sqlConnection) + { + DataContainer = dataContainer; + this.SqlConnection = sqlConnection; + + ExcludedDbs = new ArrayList(); + ExcludedDbs.Add("master"); + ExcludedDbs.Add("tempdb"); + } + + + public int GetServerVersion() + { + return DataContainer.Server.Information.Version.Major; + } + + /// + /// Maps a string devicetype to the enum Smo.DeviceType + /// Localized device type + /// + public Microsoft.SqlServer.Management.Smo.DeviceType GetDeviceType(string stringDeviceType) + { + if(String.Compare(stringDeviceType, RestoreConstants.File, StringComparison.OrdinalIgnoreCase) == 0) + { + return DeviceType.File; + } + else if (String.Compare(stringDeviceType, RestoreConstants.Url, StringComparison.OrdinalIgnoreCase) == 0) + { + return DeviceType.Url; + } + else + { + return DeviceType.LogicalDevice; + } + } + + /// + /// Maps a integer devicetype to the enum Smo.DeviceType + /// Device type + /// + public Microsoft.SqlServer.Management.Smo.DeviceType GetDeviceType(int numDeviceType) + { + if(numDeviceType == 1) + { + return DeviceType.File; + } + else if (numDeviceType == 3) + { + return DeviceType.Url; + } + else + { + return DeviceType.LogicalDevice; + } + } + + public BackupDeviceType GetPhisycalDeviceTypeOfLogicalDevice(string deviceName) + { + Enumerator en = new Enumerator(); + Request req = new Request(); + DataSet ds = new DataSet(); + ds.Locale = System.Globalization.CultureInfo.InvariantCulture; + + req.Urn = "Server/BackupDevice[@Name='" + Urn.EscapeString(deviceName) + "']"; + ds = en.Process(SqlConnection, req); + + int iCount = ds.Tables[0].Rows.Count; + if(iCount > 0) + { + BackupDeviceType ControllerType = (BackupDeviceType)(Convert.ToInt16(ds.Tables[0].Rows[0]["BackupDeviceType"], System.Globalization.CultureInfo.InvariantCulture)); + + return ControllerType; + } + else + { + throw new Exception("Unexpected error"); + } + } + + + public bool ServerHasTapes() + { + try + { + Enumerator en = new Enumerator(); + Request req = new Request(); + DataSet ds = new DataSet(); + ds.Locale = System.Globalization.CultureInfo.InvariantCulture; + + req.Urn = "Server/TapeDevice"; + + ds = en.Process(SqlConnection, req); + + int iCount = ds.Tables[0].Rows.Count; + if(iCount > 0) + { + return true; + } + return false; + } + catch(Exception) + { + return false; + } + } + + + public bool ServerHasLogicalDevices() + { + try + { + Enumerator en = new Enumerator(); + Request req = new Request(); + DataSet ds = new DataSet(); + ds.Locale = System.Globalization.CultureInfo.InvariantCulture; + + req.Urn = "Server/BackupDevice"; + ds = en.Process(SqlConnection,req); + + int iCount = ds.Tables[0].Rows.Count; + if(iCount > 0) + { + return true; + } + return false; + } + catch(Exception) + { + return false; + } + } + + + /// + /// + /// + /// + /// + public string SanitizeFileName(string name) + { + char[] result = name.ToCharArray(); + string illegalCharacters = "\\/:*?\"<>|"; + + int resultLength = result.GetLength(0); + int illegalLength = illegalCharacters.Length; + + for (int resultIndex = 0; resultIndex < resultLength; resultIndex++) + { + for (int illegalIndex = 0; illegalIndex < illegalLength; illegalIndex++) + { + if (result[resultIndex] == illegalCharacters[illegalIndex]) + { + result[resultIndex] = '_'; + } + } + } + + return new string(result); + } + + + public RecoveryModel GetRecoveryModel(string databaseName) + { + Enumerator en = null; + DataSet ds = new DataSet(); + ds.Locale = System.Globalization.CultureInfo.InvariantCulture; + Request req = new Request(); + RecoveryModel recoveryModel = RecoveryModel.Simple; + + en = new Enumerator(); + + req.Urn = "Server/Database[@Name='" + Urn.EscapeString(databaseName) + "']/Option"; + req.Fields = new string[1]; + req.Fields[0] = "RecoveryModel"; + + + ds = en.Process(this.SqlConnection, req); + + int iCount = ds.Tables[0].Rows.Count; + + if (iCount > 0) + { + recoveryModel = (RecoveryModel)(ds.Tables[0].Rows[0]["RecoveryModel"]); + } + return recoveryModel; + } + + + public string GetRecoveryModelAsString(RecoveryModel recoveryModel) + { + string szRecoveryModel = string.Empty; + + if (recoveryModel == RecoveryModel.Full) + { + szRecoveryModel = BackupConstants.RecoveryModelFull; + } + else if (recoveryModel == RecoveryModel.Simple) + { + szRecoveryModel = BackupConstants.RecoveryModelSimple; + } + else if (recoveryModel == RecoveryModel.BulkLogged) + { + szRecoveryModel = BackupConstants.RecoveryModelBulk; + } + + return szRecoveryModel; + } + + + public string GetDefaultBackupFolder() + { + string BackupFolder = ""; + + 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(SqlConnection, req); + + int iCount = ds.Tables[0].Rows.Count; + + if(iCount > 0) + { + BackupFolder = Convert.ToString(ds.Tables[0].Rows[0]["BackupDirectory"], System.Globalization.CultureInfo.InvariantCulture); + } + return BackupFolder; + } + + + public int GetMediaRetentionValue() + { + int AfterXDays = 0; + try + { + Enumerator en = new Enumerator(); + Request req = new Request(); + DataSet ds = new DataSet(); + ds.Locale = System.Globalization.CultureInfo.InvariantCulture; + + req.Urn = "Server/Configuration"; + + ds = en.Process(this.SqlConnection, req); + 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") + { + AfterXDays = Convert.ToInt32(ds.Tables[0].Rows[i]["RunValue"], System.Globalization.CultureInfo.InvariantCulture); + break; + } + } + return AfterXDays; + } + catch (Exception) + { + return AfterXDays; + } + } + + public bool IsDestinationPathValid(string path, ref bool IsFolder) + { + Enumerator en = null; + DataTable dt; + Request req = new Request(); + + en = new Enumerator(); + req.Urn = "Server/File[@FullName='" + Urn.EscapeString(path) + "']"; + + dt = en.Process(this.SqlConnection, req); + + int iCount = dt.Rows.Count; + + if (iCount > 0) + { + IsFolder = !(Convert.ToBoolean(dt.Rows[0]["IsFile"], System.Globalization.CultureInfo.InvariantCulture)); + return true; + } + else + { + IsFolder = false; + return false; + } + } + + public string GetMediaNameFromBackupSetId(int backupSetId) + { + Enumerator en = null; + DataSet ds = new DataSet(); + ds.Locale = System.Globalization.CultureInfo.InvariantCulture; + Request req = new Request(); + + int mediaId = -1; + string mediaName = string.Empty; + + en = new Enumerator(); + + req.Urn = "Server/BackupSet[@ID='" + Urn.EscapeString(Convert.ToString(backupSetId, System.Globalization.CultureInfo.InvariantCulture)) + "']"; + + try + { + ds = en.Process(SqlConnection, req); + + int iCount = ds.Tables[0].Rows.Count; + + if (iCount > 0) + { + mediaId = Convert.ToInt32(ds.Tables[0].Rows[0]["MediaSetId"], System.Globalization.CultureInfo.InvariantCulture); + ds.Clear(); + + req.Urn = "Server/BackupMediaSet[@ID='" + Urn.EscapeString(Convert.ToString(mediaId, System.Globalization.CultureInfo.InvariantCulture)) + "']"; + ds = en.Process(SqlConnection, req); + + iCount = ds.Tables[0].Rows.Count; + if (iCount > 0) + { + mediaName = Convert.ToString(ds.Tables[0].Rows[0]["Name"], System.Globalization.CultureInfo.InvariantCulture); + } + } + } + /// LPU doesn't have rights to enumerate in msdb.backupset + catch (Exception) + { + } + return mediaName; + } + + public string GetFileType(string type) + { + string result = string.Empty; + + switch (type.ToUpperInvariant()) + { + case "D": + result = RestoreConstants.Data; + break; + case "S": + result = RestoreConstants.FileStream; + break; + case "L": + result = RestoreConstants.Log; + break; + case "F": + result = RestoreConstants.FullText; + break; + default: + result = RestoreConstants.NotKnown; + break; + } + + return result; + } + + public string GetNewPhysicalRestoredFileName(string OriginalName, string dbName, bool isNewDatabase, string type, ref int fileIndex) + { + if (string.IsNullOrEmpty(OriginalName)) + { + //shall never come here + return string.Empty; + } + + string result = string.Empty; + string origfile = OriginalName; + int Idx = origfile.LastIndexOf('\\'); + string origpath = origfile.Substring(0,Idx); + + //getting the filename + string origname = origfile.Substring(Idx + 1); + Idx = origname.LastIndexOf('.'); + string origext = origname.Substring(Idx + 1); + + bool isFolder = true; + bool isValidPath = IsDestinationPathValid(origpath, ref isFolder); + + if (!isValidPath || !isFolder) + { + SMO.Server server = new SMO.Server(this.SqlConnection); + if (type != RestoreConstants.Log) + { + origpath = server.Settings.DefaultFile; + if (origpath.Length == 0) + { + origpath = server.Information.MasterDBPath; + } + } + else + { + origpath = server.Settings.DefaultLog; + if (origpath.Length == 0) + { + origpath = server.Information.MasterDBLogPath; + } + } + } + else + { + if (!isNewDatabase) + { + return OriginalName; + } + } + + if (!isNewDatabase) + { + result = origpath + "\\" + dbName + "." + origext; + } + else + { + if (0 != string.Compare(origext, "mdf", StringComparison.OrdinalIgnoreCase)) + { + result = origpath + "\\" + dbName + "_" + Convert.ToString(fileIndex, System.Globalization.CultureInfo.InvariantCulture) + "." + origext; + fileIndex++; + } + else + { + result = origpath + "\\" + dbName + "." + origext; + } + } + + return result; + } + + // TODO: This is implemented as internal property in SMO. + public bool IsLocalPrimaryReplica(string databaseName) + { + /* + SMO.Server server = new SMO.Server(this.SqlConnection); + return server.Databases[databaseName].IsLocalPrimaryReplica();*/ + return false; + } + + public bool IsHADRDatabase(string databaseName) + { + SMO.Server server = new SMO.Server(this.SqlConnection); + + // TODO: SqlConnection.ServerVersion is used instead of server.ServerVersion + if ((this.SqlConnection.ServerVersion.Major < 11) || (!server.IsHadrEnabled)) + { + return false; + } + return !string.IsNullOrEmpty(server.Databases[databaseName].AvailabilityGroupName); + } + + /// + /// Returns whether mirroring is enabled on a database or not + /// > + public bool IsMirroringEnabled(string databaseName) + { + SMO.Server server = new SMO.Server(this.SqlConnection); + // TODO: SqlConnection.ServerVersion is used instead of server.ServerVersion + if (this.SqlConnection.ServerVersion.Major < 9) + { + return false; + } + + Database db = server.Databases[databaseName]; + if (db == null || db.DatabaseEngineType != DatabaseEngineType.Standalone) + { + return false; + } + + return db.IsMirroringEnabled; + } + + public bool IsBackupTypeSupportedInReplica(string hadrDb, bool backupFileAndFileGroup, string backupTypeStr, bool copyOnly) + { + //Method should be called for HADR db + System.Diagnostics.Debug.Assert(IsHADRDatabase(hadrDb)); + + bool retVal = true; + bool localPrimaryReplica = this.IsLocalPrimaryReplica(hadrDb); + + if (localPrimaryReplica) + { + return retVal; + } + + //Set un-supported backuptype to false + if(0 == string.Compare(backupTypeStr, BackupConstants.BackupTypeFull, StringComparison.OrdinalIgnoreCase)) + { + if (!copyOnly) + { + //Full + retVal = false; + } + } + else if(0 == string.Compare(backupTypeStr, BackupConstants.BackupTypeDiff, StringComparison.OrdinalIgnoreCase)) + { + //Diff + retVal = false; + } + else if(0 == string.Compare(backupTypeStr, BackupConstants.BackupTypeTLog, StringComparison.OrdinalIgnoreCase)) + { + //Log-CopyOnly + if (copyOnly) + { + retVal = false; + } + } + + return retVal; + } + + + public bool IsDatabaseOnServer(string databaseName) + { + Enumerator en = new Enumerator(); + DataSet ds = new DataSet(); + ds.Locale = System.Globalization.CultureInfo.InvariantCulture; + Request req = new Request(); + + req.Urn = "Server/Database[@Name='" + Urn.EscapeString(databaseName) + "']"; + req.Fields = new string[1]; + req.Fields[0] = "Name"; + + ds = en.Process(SqlConnection, req); + + int iCount = ds.Tables[0].Rows.Count; + + return (iCount > 0) ? true : false; + } + + /* TODO: This is needed for Restore + /// + /// + /// + /// + /// Dictionary of Database names + public Dictionary EnumerateDatabasesOnServer(bool excludeSystemDatabase) + { + ServerComparer serverComparer = new ServerComparer(SqlConnection); + DatabaseNameEqualityComparer DbNameComparer = new DatabaseNameEqualityComparer(serverComparer); + Dictionary dictDBList = new Dictionary(DbNameComparer); + + Enumerator en = new Enumerator(); + DataSet ds = new DataSet(); + ds.Locale = System.Globalization.CultureInfo.InvariantCulture; + Request req = new Request(); + + req.Urn = "Server/Database"; + req.Fields = new string[1]; + req.Fields[0] = "Name"; + + req.OrderByList = new OrderBy[1]; + req.OrderByList[0] = new OrderBy(); + req.OrderByList[0].Field = "Name"; + req.OrderByList[0].Dir = OrderBy.Direction.Asc; + + ds = en.Process(SqlConnection, req); + + int iCount = ds.Tables[0].Rows.Count; + + for(int i = 0; i < iCount; i++) + { + string DbName = Convert.ToString(ds.Tables[0].Rows[i]["Name"], System.Globalization.CultureInfo.InvariantCulture); + + if (!excludeSystemDatabase || !this.ExcludedDbs.Contains(DbName)) + { + dictDBList.Add(DbName, DbName); + } + } + return dictDBList; + } + */ + + /* TODO: This is needed for Restore + public Dictionary EnumerateDatabasesWithBackups() + { + Enumerator en = new Enumerator(); + DataSet ds = new DataSet(); + ds.Locale = System.Globalization.CultureInfo.InvariantCulture; + Request req = new Request(); + + req.Urn = "Server/BackupSet"; + req.Fields = new string[1]; + req.Fields[0] = "DatabaseName"; + req.OrderByList = new OrderBy[1]; + req.OrderByList[0] = new OrderBy(); + req.OrderByList[0].Field = "DatabaseName"; + req.OrderByList[0].Dir = OrderBy.Direction.Asc; + + ServerComparer serverComparer = new ServerComparer(SqlConnection); + DatabaseNameEqualityComparer DbNameComparer = new DatabaseNameEqualityComparer(serverComparer); + Dictionary dict = new Dictionary(DbNameComparer); + + ds = en.Process(SqlConnection, req); + + foreach (DataRow row in ds.Tables[0].Rows) + { + string dbName = Convert.ToString(row["DatabaseName"], System.Globalization.CultureInfo.InvariantCulture); + if (!this.ExcludedDbs.Contains(dbName)) + { + //A database may contain multiple backupsets with different name. Backupset name are always case sensitive + //Removing duplicates for a database + if (!dict.ContainsKey(dbName)) + { + dict.Add(dbName, dbName); + } + } + } + return dict; + }*/ + + public void GetBackupSetTypeAndComponent(int numType, ref string Type, ref string Component) + { + switch (numType) + { + case 1: + Type = RestoreConstants.TypeFull; + Component = RestoreConstants.ComponentDatabase; + break; + case 2: + Type = RestoreConstants.TypeTransactionLog; + Component = ""; + break; + case 4: + Type = RestoreConstants.TypeFilegroup; + Component = RestoreConstants.ComponentFile; + break; + case 5: + Type = RestoreConstants.TypeDifferential; + Component = RestoreConstants.ComponentDatabase; + break; + case 6: + Type = RestoreConstants.TypeFilegroupDifferential; + Component = RestoreConstants.ComponentFile; + break; + default: + Type = RestoreConstants.NotKnown; + Component = RestoreConstants.NotKnown; + break; + } + + } + + + public void GetBackupSetTypeAndComponent(string strType, ref string Type, ref string Component) + { + string type = strType.ToUpperInvariant(); + + if (type == "D") + { + Type = RestoreConstants.TypeFull; + Component = RestoreConstants.ComponentDatabase; + } + else + { + if (type == "I") + { + Type = RestoreConstants.TypeDifferential; + Component = RestoreConstants.ComponentDatabase; + } + else + { + if (type == "L") + { + Type = RestoreConstants.TypeTransactionLog; + Component = ""; + } + else + { + if (type == "F") + { + Type = RestoreConstants.TypeFilegroup; + Component = RestoreConstants.ComponentFile; + } + else + { + if (type == "G") + { + Type = RestoreConstants.TypeFilegroupDifferential; + Component = RestoreConstants.ComponentFile; + } + else + { + Type = RestoreConstants.NotKnown; + Component = RestoreConstants.NotKnown; + } + } + } + } + } + } + + public void GetFileType(string backupType, string tempFileType, ref string fileType) + { + string bkType = backupType.ToUpperInvariant(); + string type = tempFileType.ToUpperInvariant(); + + if (bkType == "D" || bkType == "I" || bkType == "F" || bkType == "G") + { + switch (type) + { + case "D": fileType = RestoreConstants.Data; + break; + case "S": fileType = RestoreConstants.FileStream; + break; + default: fileType = RestoreConstants.NotKnown; + break; + } + } + } + + public BackupsetType GetBackupsetTypeFromBackupTypesOnDevice(int type) + { + BackupsetType Result = BackupsetType.BackupsetDatabase; + switch(type) + { + case 1: + Result = BackupsetType.BackupsetDatabase; + break; + case 2: + Result = BackupsetType.BackupsetLog; + break; + case 5: + Result = BackupsetType.BackupsetDifferential; + break; + case 4: + Result = BackupsetType.BackupsetFiles; + break; + default: + Result = BackupsetType.BackupsetDatabase; + break; + } + return Result; + } + + + public BackupsetType GetBackupsetTypeFromBackupTypesOnHistory(string type) + { + BackupsetType Result = BackupsetType.BackupsetDatabase; + switch(type) + { + case "D": + Result = BackupsetType.BackupsetDatabase; + break; + case "I": + Result = BackupsetType.BackupsetDifferential; + break; + case "L": + Result = BackupsetType.BackupsetLog; + break; + case "F": + Result = BackupsetType.BackupsetFiles; + break; + default: + Result = BackupsetType.BackupsetDatabase; + break; + } + return Result; + } + + + public DataSet GetBackupSetFiles(int backupsetId) + { + Enumerator en = new Enumerator(); + Request req = new Request(); + DataSet backupsetfiles = new DataSet(); + backupsetfiles.Locale = System.Globalization.CultureInfo.InvariantCulture; + + if(backupsetId > 0) + { + req.Urn = "Server/BackupSet[@ID='" + Urn.EscapeString(Convert.ToString(backupsetId, System.Globalization.CultureInfo.InvariantCulture)) + "']/File"; + } + else + { + req.Urn = "Server/BackupSet/File"; + } + backupsetfiles = en.Process(SqlConnection, req); + + return backupsetfiles; + } + + + public DataSet GetBackupSetById(int backupsetId) + { + + SqlExecutionModes executionMode = SqlConnection.SqlExecutionModes; + SqlConnection.SqlExecutionModes = SqlExecutionModes.ExecuteSql; + Enumerator en = new Enumerator(); + Request req = new Request(); + DataSet backupset = new DataSet(); + backupset.Locale = System.Globalization.CultureInfo.InvariantCulture; + + req.Urn = "Server/BackupSet[@ID='" + Urn.EscapeString(Convert.ToString(backupsetId, System.Globalization.CultureInfo.InvariantCulture)) + "']"; + backupset = en.Process(SqlConnection, req); + + SqlConnection.SqlExecutionModes = executionMode; + + return backupset; + + } + + + public ArrayList GetBackupSetPhysicalSources(int backupsetId) + { + + SqlExecutionModes executionMode = SqlConnection.SqlExecutionModes; + SqlConnection.SqlExecutionModes = SqlExecutionModes.ExecuteSql; + + ArrayList Sources = new ArrayList(); + + DataSet BackupSet = GetBackupSetById(backupsetId); + if(BackupSet.Tables[0].Rows.Count == 1) + { + string MediaSetID = Convert.ToString(BackupSet.Tables[0].Rows[0]["MediaSetId"], System.Globalization.CultureInfo.InvariantCulture); + + Enumerator en = new Enumerator(); + Request req = new Request(); + DataSet mediafamily = new DataSet(); + mediafamily.Locale = System.Globalization.CultureInfo.InvariantCulture; + + req.Urn = "Server/BackupMediaSet[@ID='"+Urn.EscapeString(MediaSetID)+"']/MediaFamily"; + mediafamily = en.Process(SqlConnection, req); + + if(mediafamily.Tables[0].Rows.Count > 0) + { + for(int j = 0 ; j < mediafamily.Tables[0].Rows.Count; j ++) + { + + RestoreItemSource ItemSource = new RestoreItemSource(); + + 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()); + + if (BackupDeviceType.Disk == backupDeviceType ) + { + ItemSource.RestoreItemDeviceType = DeviceType.File; + } + else if (BackupDeviceType.Url == backupDeviceType) + { + ItemSource.RestoreItemDeviceType = DeviceType.Url; + } + else + { + ItemSource.RestoreItemDeviceType = DeviceType.Tape; + } + Sources.Add(ItemSource); + } + } + } + + SqlConnection.SqlExecutionModes = executionMode; + return Sources; + } + + + public RestoreActionType GetRestoreTaskFromBackupSetType(BackupsetType type) + { + RestoreActionType Result = RestoreActionType.Database; + + switch(type) + { + case BackupsetType.BackupsetDatabase: + Result = RestoreActionType.Database; + break; + case BackupsetType.BackupsetDifferential: + Result = RestoreActionType.Database; + break; + case BackupsetType.BackupsetLog: + Result = RestoreActionType.Log; + break; + case BackupsetType.BackupsetFiles: + Result = RestoreActionType.Files; + break; + default: + Result = RestoreActionType.Database; + break; + } + return Result; + } + + + public int GetLatestBackup(string DatabaseName,string BackupSetName) + { + Enumerator en = new Enumerator(); + Request req = new Request(); + DataSet backupSets = new DataSet(); + backupSets.Locale = System.Globalization.CultureInfo.InvariantCulture; + OrderBy ob; + + req.Urn = "Server/BackupSet[@Name='"+Urn.EscapeString(BackupSetName)+"' and @DatabaseName='"+ Urn.EscapeString(DatabaseName)+"']"; + + req.OrderByList = new OrderBy[1]; + ob = new OrderBy("BackupFinishDate", OrderBy.Direction.Desc); + req.OrderByList[0] = ob; + + backupSets = en.Process(SqlConnection, req); + + if(backupSets.Tables[0].Rows.Count > 0) + { + return Convert.ToInt32(backupSets.Tables[0].Rows[0]["Position"], System.Globalization.CultureInfo.InvariantCulture); + } + else + { + return -1; + } + } + + + public ArrayList GetLatestBackupLocations(string DatabaseName) + { + ArrayList LatestLocations = new ArrayList(); + + Enumerator en = null; + DataSet ds = new DataSet(); + ds.Locale = System.Globalization.CultureInfo.InvariantCulture; + Request req = new Request(); + + en = new Enumerator(); + + req.Urn = "Server/BackupSet[@DatabaseName='" + Urn.EscapeString(DatabaseName) + "']"; + req.OrderByList = new OrderBy[1]; + req.OrderByList[0] = new OrderBy(); + req.OrderByList[0].Field = "BackupFinishDate"; + req.OrderByList[0].Dir = OrderBy.Direction.Desc; + + try + { + ds = en.Process(SqlConnection, req); + + int iCount = ds.Tables[0].Rows.Count; + if (iCount > 0) + { + string MediaSetID = Convert.ToString(ds.Tables[0].Rows[0]["MediaSetId"], System.Globalization.CultureInfo.InvariantCulture); + ds.Clear(); + req = new Request(); + req.Urn = "Server/BackupMediaSet[@ID='" + Urn.EscapeString(MediaSetID) + "']/MediaFamily"; + ds = en.Process(SqlConnection, req); + iCount = ds.Tables[0].Rows.Count; + if (iCount > 0) + { + for (int i = 0; i < iCount; i++) + { + RestoreItemSource Location = new RestoreItemSource(); + + DeviceType deviceType = (DeviceType)(Convert.ToInt16(ds.Tables[0].Rows[i]["BackupDeviceType"], System.Globalization.CultureInfo.InvariantCulture)); + string location = Convert.ToString(ds.Tables[0].Rows[i]["LogicalDeviceName"], System.Globalization.CultureInfo.InvariantCulture); + bool isLogical = (location.Length > 0); + if (false == isLogical) + { + location = Convert.ToString(ds.Tables[0].Rows[i]["PhysicalDeviceName"], System.Globalization.CultureInfo.InvariantCulture); + } + else + { + // We might receive the logical name as "logicaldevicename(physicalpath)" + // We try to get the device name out of it + int pos = location.IndexOf('('); + if (pos > 0) + { + location = location.Substring(0, pos); + } + } + Location.RestoreItemDeviceType = deviceType; + Location.RestoreItemLocation = location; + Location.IsLogicalDevice = isLogical; + LatestLocations.Add(Location); + } + } + } + } + /// LPU doesn't have rights to enumerate msdb.backupset + catch (Exception) + { + } + return LatestLocations; + } + + public string GetDefaultDatabaseForLogin(string LoginName) + { + + string DefaultDatabase = string.Empty; + + Enumerator en = new Enumerator(); + DataSet ds = new DataSet(); + ds.Locale = System.Globalization.CultureInfo.InvariantCulture; + Request req = new Request(); + + req.Urn = "Server/Login[@Name='"+Urn.EscapeString(LoginName)+"']"; + req.Fields = new string[1]; + req.Fields[0] = "DefaultDatabase"; + + ds = en.Process(SqlConnection, req); + + int iCount = ds.Tables[0].Rows.Count; + if (iCount > 0) + { + DefaultDatabase = Convert.ToString(ds.Tables[0].Rows[0]["DefaultDatabase"], System.Globalization.CultureInfo.InvariantCulture); + } + return DefaultDatabase; + + } + + + public bool IsPathExisting(string path,ref bool IsFolder) + { + Enumerator en = new Enumerator(); + DataSet ds = new DataSet(); + ds.Locale = System.Globalization.CultureInfo.InvariantCulture; + Request req = new Request(); + + req.Urn = "Server/File[@FullName='" + Urn.EscapeString(path) + "']"; + + ds = en.Process(SqlConnection, req); + + int iCount = ds.Tables[0].Rows.Count; + + if (iCount > 0) + { + IsFolder = !(Convert.ToBoolean(ds.Tables[0].Rows[0]["IsFile"], System.Globalization.CultureInfo.InvariantCulture)); + return true; + } + else + { + IsFolder = false; + return false; + } + } + + + public ArrayList IsPhysicalPathInLogicalDevice(string physicalPath) + { + + Enumerator en = new Enumerator(); + DataSet ds = new DataSet(); + ds.Locale = System.Globalization.CultureInfo.InvariantCulture; + Request req = new Request(); + ArrayList Result= null; + + int iCount = 0; + + req.Urn = "Server/BackupDevice[@PhysicalLocation='" +Urn.EscapeString(physicalPath)+ "']"; + + ds = en.Process(SqlConnection, req); + iCount = ds.Tables[0].Rows.Count; + + if(iCount > 0) + { + Result = new ArrayList(iCount); + for(int i =0 ; i < iCount ; i++) + { + Result.Add(Convert.ToString(ds.Tables[0].Rows[0]["Name"], System.Globalization.CultureInfo.InvariantCulture)); + } + } + return Result; + } + + #region Implementation - HelperFunctions - Machine Name + private const string cLocalSqlServer = "(local)"; + private const string cLocalMachineName = "."; + public string GetMachineName(string sqlServerName) + { + System.Diagnostics.Debug.Assert(sqlServerName != null); + + // special case (local) which is accepted SQL(MDAC) but by OS + if ( + (sqlServerName.ToLowerInvariant().Trim() == cLocalSqlServer) || + (sqlServerName.ToLowerInvariant().Trim() == cLocalMachineName) + ) + { + return System.Environment.MachineName; + } + + string machineName = sqlServerName; + if (sqlServerName.Trim().Length != 0) + { + // [0] = machine, [1] = instance + return sqlServerName.Split('\\')[0]; + } + else + { + // we have default instance of default machine + return machineName; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Common/DataContainer.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Common/DataContainer.cs new file mode 100644 index 00000000..a3f94386 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Common/DataContainer.cs @@ -0,0 +1,2203 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +//using System.Data; +// using System.Drawing; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Security; +using System.Xml; +// using Microsoft.AnalysisServices; +// using Microsoft.SqlServer.Common; +using Microsoft.SqlServer.Management.Common; +using Microsoft.SqlServer.Management.Diagnostics; +using Microsoft.SqlServer.Management.Sdk.Sfc; +using Microsoft.SqlServer.Management.Smo; +// using Microsoft.SqlServer.Management.UI.VSIntegration.ObjectExplorer; +using Assembly = System.Reflection.Assembly; +using System.Xml.Linq; +using Microsoft.Data.Tools.DataSets; +//This is used only for non-express sku + + +namespace Microsoft.SqlTools.ServiceLayer.Common +{ + /// + /// CDataContainer + /// + public class CDataContainer : IDisposable + { + #region Nested types + + public enum ServerType + { + SQL, + OLAP, //This type is used only for non-express sku + SQLCE, + UNKNOWN + } + + #endregion + + #region Fields + + private ServerConnection serverConnection; + private Server m_server = null; + + //This member is used for non-express sku only + // private AnalysisServices.Server m_amoServer = null; + private ISandboxLoader sandboxLoader; + + //protected XDocument m_doc = null; + protected XmlDocument m_doc = null; + private XmlDocument originalDocument = null; + private SqlOlapConnectionInfoBase connectionInfo = null; + private SqlConnectionInfoWithConnection sqlCiWithConnection; + private bool ownConnection = true; + private IManagedConnection managedConnection; + protected string serverName; + + //This member is used for non-express sku only + protected string olapServerName; + + protected string sqlceFilename; + + private ServerType serverType = ServerType.UNKNOWN; + + private Hashtable m_hashTable = null; + + private string objectNameKey = "object-name-9524b5c1-e996-4119-a433-b5b947985566"; + private string objectSchemaKey = "object-schema-ccaf2efe-8fa3-4f62-be79-62ef3cbe7390"; + + private SqlSmoObject sqlDialogSubject = null; + + private int sqlServerVersion = 0; + private int sqlServerEffectiveVersion = 0; + + + #endregion + + #region Public properties + + /// + /// gets/sets XmlDocument with parameters + /// + public XmlDocument Document + { + get + { + return this.m_doc; + } + set + { + this.m_doc = value; + + if (value != null) + { + //this.originalDocument = (XmlDocument) value.Clone(); + this.originalDocument = value; + } + else + { + this.originalDocument = null; + } + } + } + + + /// + /// returns the Hashtable that can be used to store generic information + /// + public Hashtable HashTable + { + get + { + if (m_hashTable == null) + { + m_hashTable = new Hashtable(); + } + + return m_hashTable; + } + } + + + /// + /// gets/sets SMO server object + /// + public Server Server + { + get + { + return m_server; + } + set + { + m_server = value; + } + } + + /// + /// gets/sets AMO server object + /// This member is used for non-express sku only + /// + //public AnalysisServices.Server OlapServer + //{ + // get + // { + // return m_amoServer; + // } + // set + // { + // m_amoServer = value; + // } + //} + + public ISandboxLoader SandboxLoader + { + get { return this.sandboxLoader; } + } + + /// + /// connection info that should be used by the dialogs + /// + public SqlOlapConnectionInfoBase ConnectionInfo + { + get + { + //// update the database name in the serverconnection object to set the correct database context when connected to Azure + //var conn = this.connectionInfo as SqlConnectionInfoWithConnection; + + //if (conn != null && conn.ServerConnection.DatabaseEngineType == DatabaseEngineType.SqlAzureDatabase) + //{ + // if (this.RelevantDatabaseName != null) + // { + // IComparer dbNamesComparer = ServerConnection.ConnectionFactory.GetInstance(conn.ServerConnection).ServerComparer as IComparer; + // if (dbNamesComparer.Compare(this.RelevantDatabaseName, conn.DatabaseName) != 0) + // { + // ServerConnection serverConnection = conn.ServerConnection.GetDatabaseConnection(this.RelevantDatabaseName, true, conn.AccessToken); + // ((SqlConnectionInfoWithConnection)this.connectionInfo).ServerConnection = serverConnection; + // } + // } + //} + + return this.connectionInfo; + } + } + + /// + /// returns SMO server connection object constructed off the connectionInfo. + /// This method cannot work until ConnectionInfo property has been set + /// + public ServerConnection ServerConnection + { + get + { + if (this.serverConnection == null) + { + if (this.serverType != ServerType.SQL) + { + throw new InvalidOperationException(); + } + + if (this.connectionInfo == null) + { + throw new InvalidOperationException(); + } + + + if (this.sqlCiWithConnection != null) + { + this.serverConnection = this.sqlCiWithConnection.ServerConnection; + } + else + { + SqlConnectionInfo sci = this.connectionInfo as SqlConnectionInfo; + this.serverConnection = new ServerConnection(sci); + } + } + + + return this.serverConnection; + } + } + + /// + /// returns SMO server connection object constructed off the connectionInfo. + /// This method cannot work until ConnectionInfo property has been set + /// + public SqlConnectionInfoWithConnection SqlInfoWithConnection + { + get + { + if (this.serverConnection == null) + { + if (this.serverType != ServerType.SQL) + { + throw new InvalidOperationException(); + } + + if (this.connectionInfo == null) + { + throw new InvalidOperationException(); + } + + + if (this.sqlCiWithConnection != null) + { + this.serverConnection = this.sqlCiWithConnection.ServerConnection; + } + else + { + SqlConnectionInfo sci = this.connectionInfo as SqlConnectionInfo; + this.serverConnection = new ServerConnection(sci); + } + } + + return this.sqlCiWithConnection; + } + } + + public string ServerName + { + get + { + return this.serverName; + } + set + { + this.serverName = value; + } + } + + public ServerType ContainerServerType + { + get + { + return this.serverType; + } + set + { + this.serverType = value; + } + } + + public string SqlCeFileName + { + get + { + return this.sqlceFilename; + } + set + { + this.sqlceFilename = value; + } + } + + //This member is used for non-express sku only + public string OlapServerName + { + get + { + return this.olapServerName; + } + set + { + this.olapServerName = value; + } + } + + /// + /// Whether we are creating a new object + /// + public bool IsNewObject + { + get + { + string itemType = this.GetDocumentPropertyString("itemtype"); + return (itemType.Length != 0); + } + } + + /// + /// The URN to the parent of the object we are creating/modifying + /// + public string ParentUrn + { + get + { + string result = String.Empty; + string documentUrn = this.GetDocumentPropertyString("urn"); + + if (this.IsNewObject) + { + result = documentUrn; + } + else + { + Urn urn = new Urn(documentUrn); + result = urn.Parent.ToString(); + } + + return result; + } + } + + /// + /// The URN to the object we are creating/modifying + /// + public string ObjectUrn + { + get + { + string result = String.Empty; + + if (this.IsNewObject) + { + string objectName = this.ObjectName; + string objectSchema = this.ObjectSchema; + + if (0 == objectName.Length) + { + throw new InvalidOperationException("object name is not known, so URN for object can't be formed"); + } + + if (0 == objectSchema.Length) + { + result = String.Format(CultureInfo.InvariantCulture, + "{0}/{1}[@Name='{2}']", + this.ParentUrn, + this.ObjectType, + Urn.EscapeString(objectName)); + } + else + { + result = String.Format(CultureInfo.InvariantCulture, + "{0}/{1}[@Schema='{2}' and @Name='{3}']", + this.ParentUrn, + this.ObjectType, + Urn.EscapeString(objectSchema), + Urn.EscapeString(objectName)); + } + } + else + { + result = this.GetDocumentPropertyString("urn"); + } + + return result; + } + } + + /// + /// The name of the object we are modifying + /// + public string ObjectName + { + get + { + return this.GetDocumentPropertyString(objectNameKey); + } + + set + { + this.SetDocumentPropertyValue(objectNameKey, value); + } + } + + /// + /// The schema of the object we are modifying + /// + public string ObjectSchema + { + get + { + return this.GetDocumentPropertyString(objectSchemaKey); + } + + set + { + this.SetDocumentPropertyValue(objectSchemaKey, value); + } + } + + /// + /// The type of the object we are creating (as it appears in URNs) + /// + public string ObjectType + { + get + { + // note that the itemtype property is only set for new objects + + string result = String.Empty; + string itemtype = this.GetDocumentPropertyString("itemtype"); + + // if this is not a new object + if (0 == itemtype.Length) + { + string documentUrn = this.GetDocumentPropertyString("urn"); + Urn urn = new Urn(documentUrn); + + result = urn.Type; + } + else + { + result = itemtype; + } + + return result; + } + + + } + + /// + /// The SQL SMO object that is the subject of the dialog. + /// + public SqlSmoObject SqlDialogSubject + { + get + { + SqlSmoObject result = null; + + if (this.sqlDialogSubject != null) + { + result = this.sqlDialogSubject; + } + else + { + result = this.Server.GetSmoObject(this.ObjectUrn); + } + + return result; + } + + set + { + this.sqlDialogSubject = value; + } + } + + /// + /// Whether the logged in user is a system administrator + /// + public bool LoggedInUserIsSysadmin + { + get + { + bool result = false; + + if (this.Server != null && this.Server.ConnectionContext != null) + { + result = this.Server.ConnectionContext.IsInFixedServerRole(FixedServerRoles.SysAdmin); + } + + return result; + } + } + + /// + /// Get the name of the Database that contains (or is) the subject of the dialog. + /// If no there is no relevant database, then an empty string is returned. + /// + public string RelevantDatabaseName + { + get + { + string result = String.Empty; + string urnText = this.GetDocumentPropertyString("urn"); + + if (urnText.Length != 0) + { + Urn urn = new Urn(urnText); + + while ((urn != null) && (urn.Type != "Database")) + { + urn = urn.Parent; + } + + if ((urn != null) && (urn.Type == "Database")) + { + result = urn.GetAttribute("Name"); + } + } + + return result; + } + } + + /// + /// The server major version number + /// + public int SqlServerVersion + { + get + { + if (this.sqlServerVersion == 0) + { + this.sqlServerVersion = 9; + + if ((this.ConnectionInfo != null) && (ServerType.SQL == this.ContainerServerType)) + { + Enumerator enumerator = new Enumerator(); + Urn urn = "Server/Information"; + string[] fields = new string[] { "VersionMajor" }; + DataTable dataTable = enumerator.Process(this.ConnectionInfo, new Request(urn, fields)); + + if (dataTable.Rows.Count != 0) + { + this.sqlServerVersion = (int)dataTable.Rows[0][0]; + } + } + } + + return this.sqlServerVersion; + } + + } + + /// + /// The server version the database is emulating. If database compatibility level is + /// not relevant to the subject, then this just returns the actual server version. + /// + public int EffectiveSqlServerVersion + { + get + { + if (this.sqlServerEffectiveVersion == 0) + { + this.sqlServerEffectiveVersion = 9; + + if ((this.ConnectionInfo != null) && (ServerType.SQL == this.ContainerServerType)) + { + string databaseName = this.RelevantDatabaseName; + + if (databaseName.Length != 0) + { + Enumerator enumerator = new Enumerator(); + Urn urn = String.Format("Server/Database[@Name='{0}']", Urn.EscapeString(databaseName)); + string[] fields = new string[] { "CompatibilityLevel" }; + DataTable dataTable = enumerator.Process(this.ConnectionInfo, new Request(urn, fields)); + + if (dataTable.Rows.Count != 0) + { + + CompatibilityLevel level = (CompatibilityLevel)dataTable.Rows[0][0]; + + switch (level) + { + case CompatibilityLevel.Version60: + case CompatibilityLevel.Version65: + + this.sqlServerEffectiveVersion = 6; + break; + + case CompatibilityLevel.Version70: + + this.sqlServerEffectiveVersion = 7; + break; + + case CompatibilityLevel.Version80: + + this.sqlServerEffectiveVersion = 8; + break; + + case CompatibilityLevel.Version90: + + this.sqlServerEffectiveVersion = 9; + break; + case CompatibilityLevel.Version100: + + this.sqlServerEffectiveVersion = 10; + break; + case CompatibilityLevel.Version110: + + this.sqlServerEffectiveVersion = 11; + break; + case CompatibilityLevel.Version120: + + this.sqlServerEffectiveVersion = 12; + break; + + case CompatibilityLevel.Version130: + this.sqlServerEffectiveVersion = 13; + break; + + case CompatibilityLevel.Version140: + this.sqlServerEffectiveVersion = 14; + break; + + default: + + this.sqlServerEffectiveVersion = 14; + break; + } + } + else + { + this.sqlServerEffectiveVersion = this.SqlServerVersion; + } + } + else + { + this.sqlServerEffectiveVersion = this.SqlServerVersion; + } + } + } + + return this.sqlServerEffectiveVersion; + } + } + + #endregion + + #region Constructors, finalizer + + public CDataContainer() + { + } + + /// + /// contructs the object and initializes its SQL ConnectionInfo and ServerConnection properties + /// using the specified connection info containing live connection. + /// + /// connection info containing live connection + public CDataContainer(object ciObj, bool ownConnection) + { + SqlConnectionInfoWithConnection ci = (SqlConnectionInfoWithConnection)ciObj; + if (ci == null) + { + throw new ArgumentNullException("ci"); + } + ApplyConnectionInfo(ci, ownConnection); + } + + /// + /// contructs the object and initializes its SQL ConnectionInfo and ServerConnection properties + /// using the specified connection info containing live connection. + /// + /// in addition creates a server of the given server type + /// + /// connection info containing live connection + public CDataContainer(ServerType serverType, object ciObj, bool ownConnection) + { + SqlConnectionInfoWithConnection ci = (SqlConnectionInfoWithConnection)ciObj; + if (ci == null) + { + throw new ArgumentNullException("ci"); + } + + this.serverType = serverType; + ApplyConnectionInfo(ci, ownConnection); + + if (serverType == ServerType.SQL) + { + //NOTE: ServerConnection property will constuct the object if needed + m_server = new Server(ServerConnection); + } + else + { + throw new ArgumentException("SRError.UnknownServerType(serverType.ToString()), serverType"); + } + } + + /// + /// Constructor + /// + /// Server type + /// Server name + /// true if connection is trused. If true user name and password are ignored + /// User name for not trusted connections + /// Password for not trusted connections + /// XML string with parameters + public CDataContainer(ServerType serverType, string serverName, bool trusted, string userName, SecureString password, string xmlParameters) + { + this.serverType = serverType; + this.serverName = serverName; + + if (serverType == ServerType.SQL) + { + //does some extra initialization + ApplyConnectionInfo(GetTempSqlConnectionInfoWithConnection(serverName, trusted, userName, password), true); + + //NOTE: ServerConnection property will constuct the object if needed + m_server = new Server(ServerConnection); + } + else + { + throw new ArgumentException("SRError.UnknownServerType(serverType.ToString()), serverType"); + } + + if (xmlParameters != null) + { + this.Document = GenerateXmlDocumentFromString(xmlParameters); + } + + if (ServerType.SQL == serverType) + { + this.InitializeObjectNameAndSchema(); + } + } + + /// + /// + /// + /// Data container + /// XML string with parameters + public CDataContainer(CDataContainer dataContainer, string xmlParameters) + { + Server = dataContainer.Server; + this.serverName = dataContainer.serverName; + this.serverType = dataContainer.serverType; + this.connectionInfo = dataContainer.connectionInfo; + this.ownConnection = dataContainer.ownConnection; + + this.sqlCiWithConnection = dataContainer.connectionInfo as SqlConnectionInfoWithConnection; + if (this.sqlCiWithConnection != null) + { + //we want to be notified if it is closed + this.sqlCiWithConnection.ConnectionClosed += new EventHandler(OnSqlConnectionClosed); + } + + if (xmlParameters != null) + { + XmlDocument doc = GenerateXmlDocumentFromString(xmlParameters); + this.Init(doc); + } + } + + ~CDataContainer() + { + Dispose(false); + } + + #endregion + + #region Public virtual methods + + /// + /// Initialization routine that is a convience fuction for clients with the data in a string + /// + /// The string that contains the xml data + public virtual void Init(string xmlText) + { + XmlDocument xmlDoc = GenerateXmlDocumentFromString(xmlText); + + this.Init(xmlDoc); + } + + /// + /// Overload of basic Init which takes a IServiceProvider and initializes + /// what it can of the container with elements provided by IServcieProvider + /// + /// Today this is only the IManagedProvider if available but this function + /// could be modified to init other things provided by the ServiceProvider + /// + /// + /// + public virtual void Init(XmlDocument doc, IServiceProvider site) + { + + if (site != null) + { + //see if service provider supports INodeInformation interface from the object explorer + try + { + //NOTE: we're trying to forcefully set connection information on the data container. + //If this code doesn't execute, then dc.Init call below will result in CDataContainer + //initializing its ConnectionInfo member with a new object contructed off the parameters + //in the XML doc [server name, user name etc] + IManagedConnection managedConnection = site.GetService(typeof(IManagedConnection)) as IManagedConnection; + if (managedConnection != null) + { + this.SetManagedConnection(managedConnection); + } + } + catch (Exception ex) + { + // keep the exception flowing + throw ex; + } + } + + this.Document = doc; + LoadData(); + + // finish the initialization + this.Init(doc); + } + + + /// + /// main initialization method - the object is unusable until this method is called + /// NOTE: it will ensure the ConnectionInfo and ServerConnetion objects are constructed + /// for the appropriate server types + /// + /// + public virtual void Init(XmlDocument doc) + { + //First, we read the data from XML by calling LoadData + this.Document = doc; + + LoadData(); + + //Second, create the rignt server and connection objects + if (this.serverType == ServerType.SQL) + { + //ensure that we have a valid ConnectionInfo + if (ConnectionInfo == null) + { + throw new InvalidOperationException(); + } + + if (m_server == null) + { + //NOTE: ServerConnection property will constuct the object if needed + m_server = new Server(ServerConnection); + } + } + else if (this.serverType == ServerType.SQLCE) + { + // do nothing; originally we were only distinguishing between two + // types of servers (OLAP/SQL); as a result for SQLCE we were + // executing the same codepath as for OLAP server which was + // resulting in an exception; + } + } + + /// + /// loads data into internal members from the XML document and detects the server type + /// [SQL, OLAP etc] based on the info in the XML doc + /// + public virtual void LoadData() + { + STParameters param; + bool bStatus; + + param = new STParameters(); + + param.SetDocument(m_doc); + + // DEVNOTE: chrisze 02/25/03 + // This is an ugly way to distinguish between different server types + // Maybe we should pass server type as one of the parameters? + // + bStatus = param.GetParam("servername", ref this.serverName); + + if (!bStatus || this.serverName.Length == 0) + { + + { + bStatus = param.GetParam("database", ref this.sqlceFilename); + + if (bStatus && !String.IsNullOrEmpty(this.sqlceFilename)) + { + this.serverType = ServerType.SQLCE; + } + else if (this.sqlCiWithConnection != null) + { + this.serverType = ServerType.SQL; + } + else + { + this.serverType = ServerType.UNKNOWN; + } + } + } + else + { + //OK, let's see if was specified in the parameters. It it was, use + //it to double check that it is SQL + string specifiedServerType = ""; + bStatus = param.GetParam("servertype", ref specifiedServerType); + if (bStatus) + { + if (specifiedServerType != null && "sql" != specifiedServerType.ToLowerInvariant()) + { + this.serverType = ServerType.UNKNOWN;//we know only about 3 types, and 2 of them were excluded by if branch above + } + else + { + this.serverType = ServerType.SQL; + } + } + else + { + this.serverType = ServerType.SQL; + } + } + + // Ensure there is no password in the XML document + string temp = String.Empty; + if (param.GetParam("password", ref temp)) + { + temp = null; + throw new SecurityException(); + } + + if (ServerType.SQL == this.serverType) + { + this.InitializeObjectNameAndSchema(); + } + } + + #endregion + + #region Public methods + + /// + /// we need to store it as context from the OE + /// + /// + internal void SetManagedConnection(IManagedConnection managedConnection) + { + this.managedConnection = managedConnection; + + ApplyConnectionInfo(managedConnection.Connection, true);//it will do some extra initialization + } + + /// + /// Get the named property value from the XML document + /// + /// The name of the property to get + /// The property value + public object GetDocumentPropertyValue(string propertyName) + { + object result = null; + STParameters param = new STParameters(this.Document); + + param.GetBaseParam(propertyName, ref result); + + return result; + } + + /// + /// Get the named property value from the XML document + /// + /// The name of the property to get + /// The property value + public string GetDocumentPropertyString(string propertyName) + { + object result = GetDocumentPropertyValue(propertyName); + if (result == null) + { + result = String.Empty; + } + + return (string)result; + } + + /// + /// Set the named property value in the XML document + /// + /// The name of the property to set + /// The property value + public void SetDocumentPropertyValue(string propertyName, string propertyValue) + { + STParameters param = new STParameters(this.Document); + + param.SetParam(propertyName, propertyValue); + } + + #endregion + + #region internal methods + + /// + /// Reset the data container to its state from just after it was last initialized or reset + /// + internal void Reset() + { + if (this.originalDocument != null) + { + this.Init(this.originalDocument); + } + + if (this.m_hashTable != null) + { + this.m_hashTable = new Hashtable(); + } + } + + + #endregion + + #region Private helpers + + /// + /// Get the font size specified in the STParameters structure + /// + /// The structure from which to extract font data + //private float GetFontSize(STParameters param) + //{ + // float result = 8.0f; + // string fontSize = String.Empty; + + // if (param.GetParam("fontsize", ref fontSize) && (0 != fontSize.Length)) + // { + // result = Convert.ToSingle(fontSize, CultureInfo.InvariantCulture); + // } + + // return result; + //} + + /// + /// Get the font style specified in the STParameters structure + /// + /// The structure from which to extract font data + //private FontStyle GetFontStyle(STParameters param) + //{ + // FontStyle style = FontStyle.Regular; + // string fontStyle = String.Empty; + + // if (param.GetParam("fontstyle", ref fontStyle) && (0 != fontStyle.Length)) + // { + // bool styleIsInitialized = false; + // string fontStyleUpper = fontStyle.ToUpperInvariant(); + + // if (-1 != fontStyleUpper.IndexOf("BOLD",StringComparison.Ordinal)) + // { + // style = FontStyle.Bold; + // styleIsInitialized = true; + // } + + // if (-1 != fontStyleUpper.IndexOf("ITALIC",StringComparison.Ordinal)) + // { + // if (styleIsInitialized) + // { + // style |= FontStyle.Italic; + // } + // else + // { + // style = FontStyle.Italic; + // styleIsInitialized = true; + // } + // } + + // if (-1 != fontStyleUpper.IndexOf("REGULAR",StringComparison.Ordinal)) + // { + // if (styleIsInitialized) + // { + // style |= FontStyle.Regular; + // } + // else + // { + // style = FontStyle.Regular; + // styleIsInitialized = true; + // } + // } + + // if (-1 != fontStyleUpper.IndexOf("STRIKEOUT",StringComparison.Ordinal)) + // { + // if (styleIsInitialized) + // { + // style |= FontStyle.Strikeout; + // } + // else + // { + // style = FontStyle.Strikeout; + // styleIsInitialized = true; + // } + // } + + // if (-1 != fontStyleUpper.IndexOf("UNDERLINE",StringComparison.Ordinal)) + // { + // if (styleIsInitialized) + // { + // style |= FontStyle.Underline; + // } + // else + // { + // style = FontStyle.Underline; + // styleIsInitialized = true; + // } + // } + // } + + // return style; + //} + + /// + /// Get the name and schema (if applicable) for the object we are referring to + /// + private void InitializeObjectNameAndSchema() + { + string documentUrn = this.GetDocumentPropertyString("urn"); + + if (documentUrn.Length != 0) + { + Urn urn = new Urn(documentUrn); + string name = urn.GetAttribute("Name"); + string schema = urn.GetAttribute("Schema"); + + if ((name != null) && (name.Length != 0)) + { + this.ObjectName = name; + } + + if ((schema != null) && (schema.Length != 0)) + { + this.ObjectSchema = schema; + } + } + } + + /// + /// returns SqlConnectionInfoWithConnection object constructed using our internal vars + /// + /// + private SqlConnectionInfoWithConnection GetTempSqlConnectionInfoWithConnection( + string serverName, + bool trusted, + string userName, + SecureString password) + { + SqlConnectionInfoWithConnection tempCI = new SqlConnectionInfoWithConnection(serverName); + tempCI.SingleConnection = false; + tempCI.Pooled = false; + //BUGBUG - set the right application name? + if (trusted) + { + tempCI.UseIntegratedSecurity = true; + } + else + { + tempCI.UseIntegratedSecurity = false; + tempCI.UserName = userName; + tempCI.SecurePassword = password; + } + + return tempCI; + } + + + /// + /// our handler of sqlCiWithConnection.ConnectionClosed + /// + /// + /// + private void OnSqlConnectionClosed(object sender, EventArgs e) + { + //nothing - per MRaheem we'll let user deal with this situation + } + + /// + /// stores specified connection info and performs some extra initialization steps + /// that can only be done after we have the connection information + /// + /// + private void ApplyConnectionInfo(SqlOlapConnectionInfoBase ci, bool ownConnection) + { + + this.connectionInfo = ci; + this.ownConnection = ownConnection; + + //cache the cast value. It is OK that it is null for non SQL types + this.sqlCiWithConnection = ci as SqlConnectionInfoWithConnection; + + if (this.sqlCiWithConnection != null) + { + //we want to be notified if it is closed + this.sqlCiWithConnection.ConnectionClosed += new EventHandler(OnSqlConnectionClosed); + } + } + + ///// + ///// returns string that should be specified to AMO Server.Connect method + ///// This member is used for non-express sku only + ///// + ///// + //internal string OlapConnectionString + //{ + // get + // { + // OlapConnectionInfo olapCi = this.ConnectionInfo as OlapConnectionInfo; + // if (olapCi != null) + // { + // return olapCi.ConnectionString; + // } + // else + // { + // STrace.Assert(this.olapServerName != null); + // return string.Format(CultureInfo.InvariantCulture, "Data Source={0}", this.olapServerName); + // } + + // } + //} + + private static bool MustRethrow(Exception exception) + { + bool result = false; + + switch (exception.GetType().Name) + { + case "ExecutionEngineException": + case "OutOfMemoryException": + case "AccessViolationException": + case "BadImageFormatException": + case "InvalidProgramException": + + result = true; + break; + } + + return result; + } + + /// + /// Generates an XmlDocument from a string, avoiding exploits available through + /// DTDs + /// + /// + /// + private XmlDocument GenerateXmlDocumentFromString(string sourceXml) + { + if (sourceXml == null) + { + throw new ArgumentNullException("sourceXml"); + } + if (sourceXml.Length == 0) + { + throw new ArgumentException("sourceXml"); + } + + MemoryStream memoryStream = new MemoryStream(); + StreamWriter streamWriter = new StreamWriter(memoryStream); + + // Writes the xml to the memory stream + streamWriter.Write(sourceXml); + streamWriter.Flush(); + + // Resets the stream to the beginning + memoryStream.Seek(0, SeekOrigin.Begin); + + // Creates the XML reader from the stream + // and moves it to the correct node + XmlReader xmlReader = XmlReader.Create(memoryStream); + xmlReader.MoveToContent(); + + // generate the xml document + XmlDocument xmlDocument = new XmlDocument(); + xmlDocument.PreserveWhitespace = true; + xmlDocument.LoadXml(xmlReader.ReadOuterXml()); + + return xmlDocument; + } + + #endregion + + #region ICustomTypeDescriptor + + //AttributeCollection ICustomTypeDescriptor.GetAttributes() + //{ + // return AttributeCollection.Empty; + //} + + //string ICustomTypeDescriptor.GetClassName() + //{ + // return TypeDescriptor.GetClassName(this, true); + //} + + //string ICustomTypeDescriptor.GetComponentName() + //{ + // return TypeDescriptor.GetComponentName(this, true); + //} + + //TypeConverter ICustomTypeDescriptor.GetConverter() + //{ + // return TypeDescriptor.GetConverter(GetType()); + //} + + //EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() + //{ + // return TypeDescriptor.GetDefaultEvent(GetType()); + //} + + //PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() + //{ + // return TypeDescriptor.GetDefaultProperty(GetType()); + //} + + //object ICustomTypeDescriptor.GetEditor(Type editorBaseType) + //{ + // return TypeDescriptor.GetEditor(GetType(), editorBaseType); + //} + + //EventDescriptorCollection ICustomTypeDescriptor.GetEvents() + //{ + // return EventDescriptorCollection.Empty; + //} + + //EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) + //{ + // return EventDescriptorCollection.Empty; + //} + + //PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() + //{ + // return PropertyDescriptorCollection.Empty; + //} + + //PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) + //{ + // PropertyDescriptorCollection propertyDescriptorCollection = new PropertyDescriptorCollection(null); + + // if (this.serverType == ServerType.SQL) + // { + // bool serverIsAvailable = false; + // int queryTimeout = 30; + + // try + // { + // // five seconds should be plenty to determine the network name of the server + // queryTimeout = this.m_server.ConnectionContext.StatementTimeout; + // this.m_server.ConnectionContext.StatementTimeout = 5; + // serverIsAvailable = this.m_server.Information.NetName != null; + // } + // catch (Exception e) + // { + // if (MustRethrow(e)) + // { + // throw; + // } + // } + // finally + // { + // this.m_server.ConnectionContext.StatementTimeout = queryTimeout; + // } + + // PropertyDescriptor propertyDescriptor; + // /////////////////////////////ServerEnvironment//////////////////////////////// + // // Computer Name + // try + // { + // propertyDescriptor = new PropertyDescriptorWrapper( + // serverIsAvailable ? this.m_server.Information.NetName : SR.ServerIsUnavailable, + // SR.ComputerName, + // SR.ServerEnvironment, + // SR.ComputerNameDescription); + // } + // catch (Exception e) + // { + // if (MustRethrow(e)) + // { + // throw; + // } + + // serverIsAvailable = false; + + // propertyDescriptor = new PropertyDescriptorWrapper( + // (e.Message != null) ? e.Message : SR.ServerIsUnavailable, + // SR.ComputerName, + // SR.ServerEnvironment, + // SR.ComputerNameDescription); + // } + + // propertyDescriptorCollection.Add(propertyDescriptor); + + // // Platform + // try + // { + // propertyDescriptor = new PropertyDescriptorWrapper( + // serverIsAvailable ? + // ((this.m_server.Information.Platform == null) ? + // String.Empty : + // this.m_server.Information.Platform) : + // SR.ServerIsUnavailable, + // SR.Platform, + // SR.ServerEnvironment, + // SR.PlatformDescription); + // } + // catch (Exception e) + // { + // if (MustRethrow(e)) + // { + // throw; + // } + + // serverIsAvailable = false; + + // propertyDescriptor = new PropertyDescriptorWrapper( + // (e.Message != null) ? e.Message : SR.ServerIsUnavailable, + // SR.Platform, + // SR.ServerEnvironment, + // SR.PlatformDescription); + // } + + // propertyDescriptorCollection.Add(propertyDescriptor); + + // // Operating System + // try + // { + // propertyDescriptor = new PropertyDescriptorWrapper( + // serverIsAvailable ? this.m_server.Information.OSVersion : SR.ServerIsUnavailable, + // SR.OperatingSystem, + // SR.ServerEnvironment, + // SR.OperatingSystemDescription); + // } + // catch (Exception e) + // { + // if (MustRethrow(e)) + // { + // throw; + // } + + // serverIsAvailable = false; + + // propertyDescriptor = new PropertyDescriptorWrapper( + // (e.Message != null) ? e.Message : SR.ServerIsUnavailable, + // SR.OperatingSystem, + // SR.ServerEnvironment, + // SR.OperatingSystemDescription); + // } + + // propertyDescriptorCollection.Add(propertyDescriptor); + + // // Processors + // try + // { + // propertyDescriptor = new PropertyDescriptorWrapper( + // serverIsAvailable ? + // this.m_server.Information.Processors.ToString() : + // SR.ServerIsUnavailable, + // SR.Processors, + // SR.ServerEnvironment, + // SR.ProcessorsDescription); + // } + // catch (Exception e) + // { + // if (MustRethrow(e)) + // { + // throw; + // } + + // serverIsAvailable = false; + + // propertyDescriptor = new PropertyDescriptorWrapper( + // (e.Message != null) ? e.Message : SR.ServerIsUnavailable, + // SR.Processors, + // SR.ServerEnvironment, + // SR.ProcessorsDescription); + // } + + // propertyDescriptorCollection.Add(propertyDescriptor); + + // // Operating System Memory + // try + // { + // propertyDescriptor = new PropertyDescriptorWrapper( + // serverIsAvailable ? + // this.m_server.Information.PhysicalMemory.ToString() : + // SR.ServerIsUnavailable, + // SR.OperatingSystemMemory, + // SR.ServerEnvironment, + // SR.OperatingSystemMemoryDescription); + // } + // catch (Exception e) + // { + // if (MustRethrow(e)) + // { + // throw; + // } + + // serverIsAvailable = false; + + // propertyDescriptor = new PropertyDescriptorWrapper( + // (e.Message != null) ? e.Message : SR.ServerIsUnavailable, + // SR.OperatingSystemMemory, + // SR.ServerEnvironment, + // SR.OperatingSystemMemoryDescription); + // } + + // propertyDescriptorCollection.Add(propertyDescriptor); + + // /////////////////////////////ProductCategory//////////////////////////////// + // // Product Name + // try + // { + // propertyDescriptor = new PropertyDescriptorWrapper( + // serverIsAvailable ? + // (this.m_server.Information.Product + " " + this.m_server.Information.Edition) : + // SR.ServerIsUnavailable, + // SR.ProductName, + // SR.ProductCategory, + // SR.ProductNameDescription); + // } + // catch (Exception e) + // { + // if (MustRethrow(e)) + // { + // throw; + // } + + // serverIsAvailable = false; + + // propertyDescriptor = new PropertyDescriptorWrapper( + // (e.Message != null) ? e.Message : SR.ServerIsUnavailable, + // SR.ProductName, + // SR.ProductCategory, + // SR.ProductNameDescription); + // } + + // propertyDescriptorCollection.Add(propertyDescriptor); + + // // Product Version + // try + // { + // propertyDescriptor = new PropertyDescriptorWrapper( + // serverIsAvailable ? + // (this.m_server.Information.Version + " " + this.m_server.Information.ProductLevel) : + // SR.ServerIsUnavailable, + // SR.ProductVersion, + // SR.ProductCategory, + // SR.ProductVersionDescription); + // } + // catch (Exception e) + // { + // if (MustRethrow(e)) + // { + // throw; + // } + + // serverIsAvailable = false; + + // propertyDescriptor = new PropertyDescriptorWrapper( + // (e.Message != null) ? e.Message : SR.ServerIsUnavailable, + // SR.ProductVersion, + // SR.ProductCategory, + // SR.ProductVersionDescription); + // } + + // propertyDescriptorCollection.Add(propertyDescriptor); + + // // Server Name + // try + // { + // propertyDescriptor = new PropertyDescriptorWrapper( + // serverIsAvailable ? this.m_server.Name : SR.ServerIsUnavailable, + // SR.ServerName, + // SR.ProductCategory, + // SR.ServerNameDescription); + // } + // catch (Exception e) + // { + // if (MustRethrow(e)) + // { + // throw; + // } + + // serverIsAvailable = false; + + // propertyDescriptor = new PropertyDescriptorWrapper( + // (e.Message != null) ? e.Message : SR.ServerIsUnavailable, + // SR.ServerName, + // SR.ProductCategory, + // SR.ServerNameDescription); + // } + + // propertyDescriptorCollection.Add(propertyDescriptor); + + // // Instance Name + // try + // { + // string instanceName = serverIsAvailable ? this.m_server.InstanceName : SR.ServerIsUnavailable; + + // propertyDescriptor = new PropertyDescriptorWrapper( + // (instanceName != null) ? instanceName : String.Empty, + // SR.InstanceName, + // SR.ProductCategory, + // SR.InstanceNameDescription); + // } + // catch (Exception e) + // { + // if (MustRethrow(e)) + // { + // throw; + // } + + // serverIsAvailable = false; + + // propertyDescriptor = new PropertyDescriptorWrapper( + // (e.Message != null) ? e.Message : SR.ServerIsUnavailable, + // SR.InstanceName, + // SR.ProductCategory, + // SR.InstanceNameDescription); + // } + + // propertyDescriptorCollection.Add(propertyDescriptor); + + // // Language + // try + // { + // propertyDescriptor = new PropertyDescriptorWrapper( + // serverIsAvailable ? this.m_server.Information.Language : SR.ServerIsUnavailable, + // SR.Language, + // SR.ProductCategory, + // SR.LanguageDescription); + // } + // catch (Exception e) + // { + // if (MustRethrow(e)) + // { + // throw; + // } + + // serverIsAvailable = false; + + // propertyDescriptor = new PropertyDescriptorWrapper( + // (e.Message != null) ? e.Message : SR.ServerIsUnavailable, + // SR.Language, + // SR.ProductCategory, + // SR.LanguageDescription); + // } + + // propertyDescriptorCollection.Add(propertyDescriptor); + + // // Collation + // try + // { + // propertyDescriptor = new PropertyDescriptorWrapper( + // serverIsAvailable ? this.m_server.Information.Collation : SR.ServerIsUnavailable, + // SR.Collation, + // SR.ProductCategory, + // SR.CollationDescription); + // } + // catch (Exception e) + // { + // if (MustRethrow(e)) + // { + // throw; + // } + + // serverIsAvailable = false; + + // propertyDescriptor = new PropertyDescriptorWrapper( + // (e.Message != null) ? e.Message : SR.ServerIsUnavailable, + // SR.Collation, + // SR.ProductCategory, + // SR.CollationDescription); + // } + + // propertyDescriptorCollection.Add(propertyDescriptor); + + // /////////////////////////////ConnectionCategory//////////////////////////////// + // // Database + // try + // { + // propertyDescriptor = new PropertyDescriptorWrapper( + // serverIsAvailable ? + // this.m_server.ConnectionContext.SqlConnectionObject.Database : + // SR.ServerIsUnavailable, + // SR.Database, + // SR.ConnectionCategory, + // SR.DatabaseDescription); + // } + // catch (Exception e) + // { + // if (MustRethrow(e)) + // { + // throw; + // } + + // serverIsAvailable = false; + + // propertyDescriptor = new PropertyDescriptorWrapper( + // (e.Message != null) ? e.Message : SR.ServerIsUnavailable, + // SR.Database, + // SR.ConnectionCategory, + // SR.DatabaseDescription); + // } + + // propertyDescriptorCollection.Add(propertyDescriptor); + + // // SPID + // try + // { + // propertyDescriptor = new PropertyDescriptorWrapper( + // serverIsAvailable ? + // this.m_server.ConnectionContext.ProcessID.ToString() : + // SR.ServerIsUnavailable, + // SR.Spid, + // SR.ConnectionCategory, + // SR.SpidDescription); + // } + // catch (InvalidCastException) + // { + // propertyDescriptor = new PropertyDescriptorWrapper( + // String.Empty, + // SR.Spid, + // SR.ConnectionCategory, + // SR.SpidDescription); + // } + // catch (Exception e) + // { + // if (MustRethrow(e)) + // { + // throw; + // } + + // serverIsAvailable = false; + + // propertyDescriptor = new PropertyDescriptorWrapper( + // (e.Message != null) ? e.Message : SR.ServerIsUnavailable, + // SR.Spid, + // SR.ConnectionCategory, + // SR.SpidDescription); + // } + + // propertyDescriptorCollection.Add(propertyDescriptor); + + // // Network Protocol + // try + // { + // propertyDescriptor = new PropertyDescriptorWrapper( + // serverIsAvailable ? + // (this.m_server.ConnectionContext.NetworkProtocol == NetworkProtocol.NotSpecified ? + // SR.Default : + // this.m_server.ConnectionContext.NetworkProtocol.ToString()) : + // SR.ServerIsUnavailable, + // SR.NetworkProtocol, + // SR.ConnectionCategory, + // SR.NetworkProtocolDescription); + // } + // catch (Exception e) + // { + // if (MustRethrow(e)) + // { + // throw; + // } + + // serverIsAvailable = false; + + // propertyDescriptor = new PropertyDescriptorWrapper( + // (e.Message != null) ? e.Message : SR.ServerIsUnavailable, + // SR.NetworkProtocol, + // SR.ConnectionCategory, + // SR.NetworkProtocolDescription); + // } + + // propertyDescriptorCollection.Add(propertyDescriptor); + + // // Network Packet Size + // try + // { + // propertyDescriptor = new PropertyDescriptorWrapper( + // serverIsAvailable ? + // this.m_server.ConnectionContext.PacketSize.ToString() : + // SR.ServerIsUnavailable, + // SR.NetworkPacketSize, + // SR.ConnectionCategory, + // SR.NetworkPacketSizeDescription); + // } + // catch (Exception e) + // { + // if (MustRethrow(e)) + // { + // throw; + // } + + // serverIsAvailable = false; + + // propertyDescriptor = new PropertyDescriptorWrapper( + // (e.Message != null) ? e.Message : SR.ServerIsUnavailable, + // SR.NetworkPacketSize, + // SR.ConnectionCategory, + // SR.NetworkPacketSizeDescription); + // } + + // propertyDescriptorCollection.Add(propertyDescriptor); + + // // Connect Timeout + // try + // { + // propertyDescriptor = new PropertyDescriptorWrapper( + // serverIsAvailable ? + // this.m_server.ConnectionContext.ConnectTimeout.ToString() : + // SR.ServerIsUnavailable, + // SR.ConnectTimeout, + // SR.ConnectionCategory, + // SR.ConnectTimeoutDescription); + // } + // catch (Exception e) + // { + // if (MustRethrow(e)) + // { + // throw; + // } + + // serverIsAvailable = false; + + // propertyDescriptor = new PropertyDescriptorWrapper( + // (e.Message != null) ? e.Message : SR.ServerIsUnavailable, + // SR.ConnectTimeout, + // SR.ConnectionCategory, + // SR.ConnectTimeoutDescription); + // } + + // propertyDescriptorCollection.Add(propertyDescriptor); + + // // Statement Timeout + // try + // { + // propertyDescriptor = new PropertyDescriptorWrapper( + // serverIsAvailable ? + // this.m_server.ConnectionContext.StatementTimeout.ToString() : + // SR.ServerIsUnavailable, + // SR.StatementTimeout, + // SR.ConnectionCategory, + // SR.StatementTimeoutDescription); + // } + // catch (Exception e) + // { + // if (MustRethrow(e)) + // { + // throw; + // } + + // serverIsAvailable = false; + + // propertyDescriptor = new PropertyDescriptorWrapper( + // (e.Message != null) ? e.Message : SR.ServerIsUnavailable, + // SR.StatementTimeout, + // SR.ConnectionCategory, + // SR.StatementTimeoutDescription); + // } + + // propertyDescriptorCollection.Add(propertyDescriptor); + + // // Encryption + // try + // { + // string encryptAsString = SR.NoString; + + // if (serverIsAvailable) + // { + // if (this.m_server.ConnectionContext.EncryptConnection) + // { + // encryptAsString = SR.YesString; + // } + // } + // else + // { + // encryptAsString = SR.ServerIsUnavailable; + // } + + // propertyDescriptor = new PropertyDescriptorWrapper( + // encryptAsString, + // SR.EncryptedConnection, + // SR.ConnectionCategory, + // SR.EncryptedConnectionDescription); + // } + // catch (InvalidCastException) + // { + // propertyDescriptor = new PropertyDescriptorWrapper( + // String.Empty, + // SR.EncryptedConnection, + // SR.ConnectionCategory, + // SR.EncryptedConnectionDescription); + // } + // catch (Exception e) + // { + // if (MustRethrow(e)) + // { + // throw; + // } + + // serverIsAvailable = false; + + // propertyDescriptor = new PropertyDescriptorWrapper( + // (e.Message != null) ? e.Message : SR.ServerIsUnavailable, + // SR.EncryptedConnection, + // SR.ConnectionCategory, + // SR.EncryptedConnectionDescription); + // } + + // propertyDescriptorCollection.Add(propertyDescriptor); + + // /////////////////////////////Authentication//////////////////////////////// + // // Authentication + // try + // { + // string authenticationType; + + // if (!serverIsAvailable) + // { + // authenticationType = SR.ServerIsUnavailable; + // } + // else if (this.m_server.ConnectionContext.LoginSecure) + // { + // authenticationType = SR.WindowsAuthentication; + // } + // else + // { + // authenticationType = SR.SqlServerAuthentication; + // } + + // propertyDescriptor = new PropertyDescriptorWrapper( + // authenticationType, + // SR.AuthenticationMethod, + // SR.AuthenticationCategory, + // SR.AuthenticationMethodDescription); + + // } + // catch (PropertyNotAvailableException e) + // { + // propertyDescriptor = new PropertyDescriptorWrapper( + // (e.Message == null) ? String.Empty : e.Message, + // SR.AuthenticationMethod, + // SR.AuthenticationCategory, + // SR.AuthenticationMethodDescription); + + // } + // catch (Exception exception) + // { + // if (MustRethrow(exception)) + // { + // throw; + // } + + // serverIsAvailable = false; + + // propertyDescriptor = new PropertyDescriptorWrapper( + // (exception.Message != null) ? exception.Message : SR.ServerIsUnavailable, + // SR.EncryptedConnection, + // SR.ConnectionCategory, + // SR.EncryptedConnectionDescription); + // } + + // propertyDescriptorCollection.Add(propertyDescriptor); + + // // TrueLogin + // try + // { + // propertyDescriptor = new PropertyDescriptorWrapper( + // serverIsAvailable ? this.m_server.ConnectionContext.TrueLogin : SR.ServerIsUnavailable, + // SR.UserName, + // SR.AuthenticationCategory, + // SR.UserNameDescription); + // } + // catch (Exception e) + // { + // if (MustRethrow(e)) + // { + // throw; + // } + + // serverIsAvailable = false; + + // propertyDescriptor = new PropertyDescriptorWrapper( + // (e.Message != null) ? e.Message : SR.ServerIsUnavailable, + // SR.UserName, + // SR.AuthenticationCategory, + // SR.UserNameDescription); + // } + + // propertyDescriptorCollection.Add(propertyDescriptor); + // } + // else if (!Utils.IsSsmsMinimalSet()) + // { + // if (this.serverType == ServerType.OLAP) + // { + // PropertyDescriptor propertyDescriptor; + + // /////////////////////////////ServerEnvironment//////////////////////////////// + + // /////////////////////////////ProductCategory//////////////////////////////// + // // Product Version + // propertyDescriptor = new PropertyDescriptorWrapper(this.m_amoServer.Version, SR.ProductVersion, SR.ProductCategory, SR.ProductVersionDescription); + // propertyDescriptorCollection.Add(propertyDescriptor); + // // Server Name + // propertyDescriptor = new PropertyDescriptorWrapper(this.m_amoServer.Name, SR.ServerName, SR.ProductCategory, SR.ServerNameDescription); + // propertyDescriptorCollection.Add(propertyDescriptor); + // // Language + // int langAsInt = Convert.ToInt32(this.m_amoServer.ServerProperties[@"Language"].Value, CultureInfo.InvariantCulture); + // if (langAsInt > 0) + // { + // try + // { + // propertyDescriptor = new PropertyDescriptorWrapper(new CultureInfo(langAsInt).ToString(), SR.Language, SR.ProductCategory, SR.LanguageDescription); + // propertyDescriptorCollection.Add(propertyDescriptor); + // } + // catch //eat it - CultureInfo might not be creatable from this ID + // { } + // } + // // Collation + // propertyDescriptor = new PropertyDescriptorWrapper(this.m_amoServer.ServerProperties[@"CollationName"].Value, SR.Collation, SR.ProductCategory, SR.CollationDescription); + // propertyDescriptorCollection.Add(propertyDescriptor); + + // /////////////////////////////ConnectionCategory//////////////////////////////// + // // Database + // propertyDescriptor = new PropertyDescriptorWrapper(this.ConnectionInfo.DatabaseName, SR.Database, SR.ConnectionCategory, SR.DatabaseDescription); + // propertyDescriptorCollection.Add(propertyDescriptor); + // // Connect Timeout + // propertyDescriptor = new PropertyDescriptorWrapper(this.ConnectionInfo.ConnectionTimeout, SR.ConnectTimeout, SR.ConnectionCategory, SR.ConnectTimeoutDescription); + // propertyDescriptorCollection.Add(propertyDescriptor); + // // Execution Timeout + // propertyDescriptor = new PropertyDescriptorWrapper(this.ConnectionInfo.QueryTimeout, SR.StatementTimeout, SR.ConnectionCategory, SR.StatementTimeoutDescription); + // propertyDescriptorCollection.Add(propertyDescriptor); + + // //whether connection is encrypted or not + // OlapConnectionInfo olapCi = this.ConnectionInfo as OlapConnectionInfo; + // if (olapCi != null) + // { + // string encryptAsString = SR.NoString; + // if (olapCi.EncryptConnection) + // { + // encryptAsString = SR.YesString; + // } + // propertyDescriptor = new PropertyDescriptorWrapper(encryptAsString, SR.EncryptedConnection, SR.ConnectionCategory, SR.EncryptedConnectionDescription); + // propertyDescriptorCollection.Add(propertyDescriptor); + // } + + // // Authentication + // if (this.ConnectionInfo.UseIntegratedSecurity) + // propertyDescriptor = new PropertyDescriptorWrapper(SR.WindowsAuthentication, SR.AuthenticationMethod, SR.AuthenticationCategory, SR.AuthenticationMethodDescription); + // else + // propertyDescriptor = new PropertyDescriptorWrapper(SR.SqlServerAuthentication, SR.AuthenticationMethod, SR.AuthenticationCategory, SR.AuthenticationMethodDescription); + // propertyDescriptorCollection.Add(propertyDescriptor); + // // TrueLogin + // propertyDescriptor = new PropertyDescriptorWrapper(this.ConnectionInfo.UserName, SR.UserName, SR.AuthenticationCategory, SR.UserNameDescription); + // propertyDescriptorCollection.Add(propertyDescriptor); + // } + // else if (this.serverType == ServerType.SQLCE) + // { + // PropertyDescriptor propertyDescriptor; + + // // Create an instance of SQLCE engine through reflection + // // + // Assembly asm = SfcUtility.LoadSqlCeAssembly( + // "Microsoft.SqlServerCe.Client.dll"); + // Type type = asm.GetType("Microsoft.SqlServerCe.Client.SqlCeEngine", true); + + // // Create SqlCeEngine instance + // // + // ConstructorInfo constructor = type.GetConstructor(new Type[] { }); + // object instance = constructor.Invoke(new object[] { }); + + // // Call GetDatabaseProperties + // // + // Type[] types = { typeof(String) }; + // MethodInfo mi = type.GetMethod("GetDatabaseProperties", types); + + // instance = mi.Invoke(instance, new object[] { SqlCeFileName }); + + // // Call the accessors on the DatabaseProperties class + // // + // type = asm.GetType("Microsoft.SqlServerCe.Client.DatabaseProperties", true); + + // PropertyInfo pi = type.GetProperty("Platform"); + // string platform = (string)pi.GetValue(instance, new object[] { }); + + // pi = type.GetProperty("OSVersion"); + // string osVersion = (string)pi.GetValue(instance, new object[] { }); + + // pi = type.GetProperty("Version"); + // string sqlCeVersion = (string)pi.GetValue(instance, new object[] { }); + + // pi = type.GetProperty("ClrVersion"); + // string clrVersion = (string)pi.GetValue(instance, new object[] { }); + + // // Platform + // propertyDescriptor = new PropertyDescriptorWrapper(platform, SR.Platform, SR.ServerEnvironment, SR.PlatformDescription); + // propertyDescriptorCollection.Add(propertyDescriptor); + + // // Operating System + // propertyDescriptor = new PropertyDescriptorWrapper(osVersion, SR.OperatingSystem, SR.ServerEnvironment, SR.OperatingSystemDescription); + // propertyDescriptorCollection.Add(propertyDescriptor); + + // // CLR version + // propertyDescriptor = new PropertyDescriptorWrapper(clrVersion, SR.ClrVersion, SR.ServerEnvironment, SR.ClrVersionDescription); + // propertyDescriptorCollection.Add(propertyDescriptor); + + // // Product Name + // propertyDescriptor = new PropertyDescriptorWrapper(SR.SqlServerCeName, SR.ProductName, SR.ProductCategory, SR.ProductNameDescriptionCE); + // propertyDescriptorCollection.Add(propertyDescriptor); + + // // Product Version + // propertyDescriptor = new PropertyDescriptorWrapper(sqlCeVersion, SR.ProductVersion, SR.ProductCategory, SR.ProductVersionDescriptionCE); + // propertyDescriptorCollection.Add(propertyDescriptor); + // } + // } + // return propertyDescriptorCollection; + //} + + //object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) + //{ + // return this; + //} + + #endregion + + #region IDisposable + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// MUST be called, as we'll be closing SQL connection inside this call + /// + private void Dispose(bool disposing) + { + try + { + //take care of live SQL connection + if (this.sqlCiWithConnection != null) + { + this.sqlCiWithConnection.ConnectionClosed -= new EventHandler(OnSqlConnectionClosed); + + if (disposing) + { + //if we have the managed connection interface, then use it to disconnect. + //Otherwise, Dispose on SqlConnectionInfoWithConnection should disconnect + if (this.managedConnection != null) + { + //in this case we have gotten sqlCiWithConnection as this.managedConnection.Connection + if (this.ownConnection) + { + this.managedConnection.Close(); + } + this.managedConnection = null; + } + else + { + if (this.ownConnection) + { + this.sqlCiWithConnection.Dispose();//internally will decide whether to disconnect or not + } + } + this.sqlCiWithConnection = null; + } + else + { + this.managedConnection = null; + this.sqlCiWithConnection = null; + } + } + else if (this.managedConnection != null) + { + if (disposing && this.ownConnection) + { + this.managedConnection.Close(); + } + this.managedConnection = null; + } + } + catch (Exception exToEat) + { + } + } + + #endregion + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Common/IManagedConnection.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Common/IManagedConnection.cs new file mode 100644 index 00000000..492ad54e --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Common/IManagedConnection.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections; +using Microsoft.SqlServer.Management.Common; +using Microsoft.SqlServer.Management.Diagnostics; + +namespace Microsoft.SqlTools.ServiceLayer.Common +{ + + /// + /// Provides connection and enumerator context for a node + /// + public interface IManagedConnection : IDisposable + { + /// + /// Connection information. + /// + SqlOlapConnectionInfoBase Connection + { + get; + } + + /// + /// Free any resources for this connection + /// + void Close(); + } + + /// + /// interface used by the objectexplorer. Allows us to "pool" the main connection + /// + internal interface IManagedConnection2 : IManagedConnection + { + } + + /// + /// Implementation of IManagedConnection. Allows the use of a direct or indirect connection + /// in the object explorer that takes care of the connection. + /// + internal class ManagedConnection : IManagedConnection2 + { + #region private members + private bool connectionAddedToActiveConnections = false; + private bool closeOnDispose = false; + private SqlOlapConnectionInfoBase connection; + private bool closed = false; + #endregion + + #region Construction + /// + /// Create a new managed connection + /// + /// connection wish to manage + public ManagedConnection(SqlOlapConnectionInfoBase connection) + : this(connection, false) + { + } + /// + /// create a new managed connection. + /// + /// connection + /// true if we are going to try and reuse the + /// connection if possible + public ManagedConnection(SqlOlapConnectionInfoBase sourceConnection, bool attemptToPool) + { + // parameter check + if (sourceConnection == null) + { + throw new ArgumentNullException("sourceConnection"); + } + + // see if the connection can restrict access (single user mode) + IRestrictedAccess access = sourceConnection as IRestrictedAccess; + // see if it is cloneable + ICloneable cloneable = sourceConnection as ICloneable; + lock (ActiveConnections) + { + // if it's not single user mode then we can see if the object can be cloned + if (access == null || access.SingleConnection == false) + { + // if we are going to attempt to pool, see if the connection is in use + if (attemptToPool && !ActiveConnections.Contains(SharedConnectionUtil.GetConnectionKeyName(sourceConnection))) + { + // add it to the hashtable to indicate use. + ActiveConnections.Add(SharedConnectionUtil.GetConnectionKeyName(sourceConnection), sourceConnection); + this.connection = sourceConnection; + this.closeOnDispose = false; + this.connectionAddedToActiveConnections = true; + } + else if (cloneable != null) + { + this.connection = (SqlOlapConnectionInfoBase)cloneable.Clone(); + this.closeOnDispose = true; + } + else if (sourceConnection is SqlConnectionInfoWithConnection) + { + this.connection = ((SqlConnectionInfoWithConnection)sourceConnection).Copy(); + this.closeOnDispose = true; + } + } + } + // if everything else has failed just use to passed in connection. + if (this.connection == null) + { + this.connection = sourceConnection; + } + + // always set the lock timeout to prevent the shell from not responding + if (this.connection is SqlConnectionInfoWithConnection) + { + // set lock_timeout to 10 seconds + ((SqlConnectionInfoWithConnection)this.connection).ServerConnection.LockTimeout = 10; + } + } + #endregion + + #region IDisposable implementation + public void Dispose() + { + Close(); + } + #endregion + + #region IManagedConnection implementation + /// + /// Connection + /// + public SqlOlapConnectionInfoBase Connection + { + get + { + return this.connection; + } + } + + /// + /// Close the current connection if applicable. + /// + public void Close() + { + if (this.closed) + return; + + if (this.closeOnDispose) + { + IDisposable disp = this.connection as IDisposable; + if (disp != null) + { + disp.Dispose(); + } + } + else + { + // if we are not closing the connection and it is a sql connection then ensure it + // is left in the master database. + SqlConnectionInfoWithConnection sqlConnection = this.connection as SqlConnectionInfoWithConnection; + if (sqlConnection != null && sqlConnection.ServerConnection.DatabaseEngineType == DatabaseEngineType.Standalone) + { + try + { + sqlConnection.ServerConnection.ExecuteNonQuery("use [master]"); + } + // don't error if this fails + catch + { } + } + } + if (this.connectionAddedToActiveConnections) + { + lock (ActiveConnections) + { + ActiveConnections.Remove(SharedConnectionUtil.GetConnectionKeyName(connection)); + } + } + + this.connection = null; + this.closed = true; + } + #endregion + + #region static helpers + /// + /// hashtable we use to keep track of actively used main connections + /// + private static Hashtable activeConnections = null; + private Hashtable ActiveConnections + { + get + { + if (activeConnections == null) + { + activeConnections = new Hashtable(); + } + return activeConnections; + } + } + #endregion + } +} \ No newline at end of file diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Common/ISandboxLoader.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Common/ISandboxLoader.cs new file mode 100644 index 00000000..b7d2adf1 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Common/ISandboxLoader.cs @@ -0,0 +1,31 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; + +namespace Microsoft.SqlTools.ServiceLayer.Common +{ + /// + /// To access DataModelingSandbox from NavigableItem + /// + public interface ISandboxLoader + { + /// + /// Get sandbox + /// + /// DataModelingSandbox object associated with this NavigableItem + object GetSandbox(); + + /// + /// Refresh sandbox data associated with this NavigableItem + /// + void RefreshSandboxData(); + + /// + /// Delete sandbox from cache + /// + void DeleteSandbox(); + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Common/STParameters.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Common/STParameters.cs new file mode 100644 index 00000000..fc9f0488 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Common/STParameters.cs @@ -0,0 +1,488 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; +using System.Xml; +using System.Collections; +using System.Diagnostics; + +namespace Microsoft.SqlTools.ServiceLayer.Common +{ + /// + /// SqlTools Parameters, used to define what goes into starting up a Workbench Form + /// AKA a dbCommander, AKA a "dialog" + /// These parameters are xml snippets + /// + public class STParameters + { + public XmlDocument m_doc; + + /// + /// The data type we are interested in + /// + public enum STType { eNULL, eInt, eLong, eString }; + + /// + /// default constructor + /// + public STParameters() + { + // + // TODO: Add constructor logic here + // + } + + /// + /// Constructor + /// + /// The xml snippet used to control the dbCommander + public STParameters(XmlDocument xmlDoc) + { + m_doc = xmlDoc; + } + + /// + /// Changing the xml snippet we are using + /// + /// the new xml snippet + public void SetDocument(XmlDocument xmlDoc) + { + m_doc = xmlDoc; + } + + /// + /// Access to the xml we are using for dbCommander parameters + /// + /// our current parameters + public XmlDocument GetDocument() + { + return m_doc; + } + + /// + /// Search for an xml tag, and return its value + /// + /// the xml tag name + /// the value of that tag + /// flag that is true if the data was found, false if not + public bool GetBaseParam(string parameterName, ref object value) + { + XmlNodeList nodeList = null; + bool parameterExists; + + if (m_doc == null) + return false; + + parameterExists = false; + nodeList = m_doc.GetElementsByTagName(parameterName); + + if (nodeList.Count > 1) + { + value = null; + } + else if (nodeList.Count != 0) // anything there? + { + try + { + XmlNode node = nodeList.Item(0); + if (null != node) + { + value = node.InnerText as object; + parameterExists = true; + } + } + catch (Exception /*e*/) + { + } + + } + + return parameterExists; + } + + /// + /// Finds an existing xml tag, and sets it to a new value, or if the tag is not found + /// create it and set it's value + /// + /// tag name + /// new value + /// flag that is true if the tag was set, false if not + public bool SetBaseParam(string parameterName, object value) + { + XmlNodeList nodeList; + bool success = false; + + nodeList = m_doc.GetElementsByTagName(parameterName); + + if (nodeList.Count == 1) + { + try + { + nodeList.Item(0).InnerText = (string)value; + success = true; + } + catch (InvalidCastException /*e*/) + { + success = false; + } + + } + + if (nodeList.Count == 0) + { + try + { + XmlElement xmlElement = m_doc.CreateElement(parameterName); + XmlNode root = m_doc.DocumentElement; + + nodeList = m_doc.GetElementsByTagName("params"); + + if (nodeList.Count == 1 && value is string) + { + xmlElement.InnerText = (string)value; + nodeList.Item(0).InsertAfter(xmlElement, nodeList.Item(0).LastChild); + + success = true; + } + } + catch (Exception e) + { + string sz = e.ToString(); + success = false; + } + + } + + return success; + + } + + /// + /// Get back an interger parameter. + /// NOTE: if the tag exists, but it contains non-numeric data, this will throw + /// An exception of type 'System.FormatException' + /// with Additional information: Could not find any parsible digits. + /// + /// xml tag name for the parameter of interest + /// out value of parameter + /// flag that is true if the data was found, false if not + public bool GetParam(string parameterName, out int value) + { + bool parameterExists = false; + + value = 0; + + try + { + object oAux = null; + + if (parameterExists = GetBaseParam(parameterName, ref oAux)) + { + try + { + value = Convert.ToInt32((string)oAux, 10); // data is always a string, it is the value of XmlNode.InnerText + } + catch (FormatException e) + { + Debug.WriteLine(String.Format(System.Globalization.CultureInfo.CurrentCulture, "Non numeric data in tag: {0}", parameterName)); + Debug.WriteLine(e.Message); + throw; + } + } + } + catch (InvalidCastException /*e*/) + { + } + + return parameterExists; + } + + /// + /// Accessor for a boolean parameter + /// NOTE: if the tag exists, but it contains non-numeric data, this will throw + /// An exception of type 'System.FormatException' + /// with Additional information: Could not find any parsible digits. + /// + /// xml tag name for the parameter of interest + /// out value of parameter + /// flag that is true if the data was found, false if not + public bool GetParam(string parameterName, ref bool value) + { + bool parameterExists = false; + + value = false; + + try + { + object oAux = null; + if (parameterExists = GetBaseParam(parameterName, ref oAux)) + { + try + { + value = Convert.ToBoolean((string)oAux, System.Globalization.CultureInfo.InvariantCulture); // data is always a string, it is the value of XmlNode.InnerText + } + catch (FormatException e) + { + Debug.WriteLine(String.Format(System.Globalization.CultureInfo.CurrentCulture, "Non boolean data in tag: {0}", parameterName)); + Debug.WriteLine(e.Message); + throw; + } + } + } + catch (InvalidCastException /*e*/) + { + } + + return parameterExists; + } + + /// + /// Accessor to a string parameter + /// + /// xml tag name for the parameter of interest + /// out value of parameter + /// flag that is true if the data was found, false if not + public bool GetParam(string parameterName, ref string value) + { + bool parameterExists; + + value = ""; + parameterExists = false; + + try + { + object oAux = null; + + if (parameterExists = GetBaseParam(parameterName, ref oAux)) + { + value = (string)oAux; + } + } + catch (InvalidCastException /*e*/) + { + } + + return parameterExists; + + } + + /// + /// Accessor to long parameter (Int64) + /// NOTE: if the tag exists, but it contains non-numeric data, this will throw + /// An exception of type 'System.FormatException' + /// with Additional information: Could not find any parsible digits. + /// + /// xml tag name for the parameter of interest + /// out value of parameter + /// flag that is true if the data was found, false if not + public bool GetParam(string parameterName, out long value) + { + bool parameterExists = false; + + value = 0; + + try + { + object oAux = null; + + if (parameterExists = GetBaseParam(parameterName, ref oAux)) + { + try + { + value = Convert.ToInt64((string)oAux, 10); // data is always a string, it is the value of XmlNode.InnerText + } + catch (FormatException e) + { + Debug.WriteLine(String.Format(System.Globalization.CultureInfo.CurrentCulture, "Non numeric data in tag: {0}", parameterName)); + Debug.WriteLine(e.Message); + throw; + } + } + } + catch (InvalidCastException /*e*/) + { + } + + return parameterExists; + + } + + + /// + /// Set an int (Int32) parameter + /// + /// tag name for parameter + /// integer value + /// true if set was successful, false if not + public bool SetParam(string parameterName, int value) + { + bool success; + + success = SetBaseParam(parameterName, (object)value); + + return success; + + } + + /// + /// Set a string parameter + /// + /// tag name for parameter + /// string value + /// true if set was successful, false if not + public bool SetParam(string parameterName, string value) + { + bool success; + + success = SetBaseParam(parameterName, (object)value); + + return success; + + } + + /// + /// Set a long (Int64) parameter + /// + /// tag name for parameter + /// long value + /// true if set was successful, false if not + public bool SetParam(string parameterName, long value) + { + bool success; + + success = SetBaseParam(parameterName, (object)value); + + return success; + + } + + /// + /// Get a string collection parameter + /// + /// name of collection + /// collection that gets filled up with parameters + /// true if we want to get at inner nodes, false if not + /// true if parameter(s) exist + public bool GetParam(string parameterName, System.Collections.Specialized.StringCollection list, bool getInnerXml) + { + /// necessary for OALP objects path that is in an XML form + if (true == getInnerXml) + { + XmlNodeList nodeList; + bool parameterExists; + long lCount; + + parameterExists = false; + nodeList = m_doc.GetElementsByTagName(parameterName); + + list.Clear(); + + lCount = nodeList.Count; + + if (lCount > 0) + { + parameterExists = true; + + for (long i = 0; i < lCount; i++) + { + list.Add(nodeList.Item((int)i).InnerXml); + } + } + else + { + parameterExists = false; + } + + return parameterExists; + } + else + { + return GetParam(parameterName, list); + } + } + + /// + /// Access to a collection of parameters + /// + /// name of collection + /// list to fill with parameters + /// parameter(s) exist + public bool GetParam(string parameterName, System.Collections.Specialized.StringCollection list) + { + XmlNodeList nodeList; + bool parameterExists; + long lCount; + + parameterExists = false; + nodeList = m_doc.GetElementsByTagName(parameterName); + + list.Clear(); + + lCount = nodeList.Count; + + if (lCount > 0) + { + parameterExists = true; + + for (long i = 0; i < lCount; i++) + { + list.Add(nodeList.Item((int)i).InnerText); + } + } + else + { + parameterExists = false; + } + + return parameterExists; + } + + public bool GetParam(string parameterName, ref ArrayList list) + { + System.Collections.Specialized.StringCollection stringList = new System.Collections.Specialized.StringCollection(); + bool parameterExists = GetParam(parameterName, stringList); + list.Clear(); + if (!parameterExists) + { + return false; + } + else + { + for (int i = 0; i < stringList.Count; i++) + { + list.Add(stringList[i]); + } + return true; + } + } + + /// + /// This function does nothing but return false + /// + /// ignored + /// ignored + /// always false + public bool GetParamType(string parameterName, STType type) + { + bool whatever = false; + + return whatever; + } + + /// + /// This function does nothing but return false + /// + /// ignored + /// ignored + /// always false + public bool SetParamType(string parameterName, STType type) + { + bool whatever = false; + + return whatever; + } + + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Common/SharedConnectionUtil.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Common/SharedConnectionUtil.cs new file mode 100644 index 00000000..cef590d5 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Common/SharedConnectionUtil.cs @@ -0,0 +1,108 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; +//using Microsoft.SqlServer.Management.AzureCredential; +using Microsoft.SqlServer.Management.Common; +//using Microsoft.SqlServer.Management.Smo.RegSvrEnum; +using SFC = Microsoft.SqlServer.Management.Sdk.Sfc; +//using Microsoft.SqlServer.StorageClient; +//using Microsoft.SqlServer.Management.SqlMgmt; + +/// +/// Summary description for SharedConectionUtil +/// Moved GetConnectionName static call in a public class acessible for both +/// OEXM and OE +/// +namespace Microsoft.SqlTools.ServiceLayer.Common +{ + internal class SharedConnectionUtil + { + public SharedConnectionUtil() + { + } + + /// + /// + /// + /// + /// + public static string GetConnectionKeyName(SqlOlapConnectionInfoBase ci) + { + + //// Note that these strings are not localized. The returned string is used by OE in a + //// hash of connections so it can tell if it already has such a connection open. This + //// string is never seen by the user. For the string seen by the user, see + //// ServerNameHandler.cs. + string displayName = String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} (", ci.ServerName); + + if (!string.IsNullOrEmpty(ci.DatabaseName)) + { + displayName += ", " + ci.DatabaseName; + } + + return displayName; + + //switch (ci.ServerType) + //{ + // case ConnectionType.AzureStorage: + // AzureStorageConnectionInfo azureCI = ci as AzureStorageConnectionInfo; + // displayName = "AzureStorage," + azureCI.BlobClient.BaseUri; + // break; + // case ConnectionType.AzureAccount: + // if (ci is CertificateBasedAuthenticationInfo) + // { + // displayName = "AzureSubscription," + (ci as CertificateBasedAuthenticationInfo).SubscriptionId; + // } + // else + // { + // displayName = "AzureSubscription"; + // } + // break; + // case ConnectionType.Sql: + // displayName += "SQLServer"; + // SqlConnectionInfo sqlCi = ci as SqlConnectionInfo; + // if (sqlCi.UseIntegratedSecurity == true) + // { + // displayName += ", trusted"; + // } + // else + // { + // displayName += String.Format(System.Globalization.CultureInfo.InvariantCulture, ", user = {0}", sqlCi.UserName); + // //In Cloud a user can have access to only a few UDBs without access to master DB + // // and hence need to show different OE hierarchy trees for each DB + // //Same is the case with a contained user. + + + // if (ServerInfoCache.GetDatabaseEngineType(ci.ServerName) == DatabaseEngineType.SqlAzureDatabase + // || SFC.ExecuteSql.GetDatabaseEngineType(ci) == DatabaseEngineType.SqlAzureDatabase + // || SFC.ExecuteSql.IsContainedAuthentication(ci)) + // { + // if (!string.IsNullOrEmpty(ci.DatabaseName)) + // { + // displayName += ", " + ci.DatabaseName; + // } + // } + // } + // break; + // case ConnectionType.Olap: + // displayName += "OLAP"; + // break; + // case ConnectionType.SqlCE: + // displayName += "SqlCE"; + // break; + // case ConnectionType.ReportServer: + // displayName += "Rs"; + // displayName += String.Format(System.Globalization.CultureInfo.InvariantCulture, ", connection = {0}", ci.ConnectionString); + // break; + // case ConnectionType.IntegrationServer: + // displayName += "SSIS"; + // break; + //} + //displayName += ")"; + //return displayName; + } + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Contracts/BackupInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Contracts/BackupInfo.cs index d54009c8..f6968c2e 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Contracts/BackupInfo.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Contracts/BackupInfo.cs @@ -3,10 +3,57 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using System.Collections; +using System.Collections.Generic; + namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts { public class BackupInfo { - public string BackupType { get; set; } + /// + /// Name of the datbase to perfom backup + /// + public string DatabaseName { get; set; } + + /// + /// Component to backup - Database or Files + /// + public int BackupComponent { get; set; } + + /// + /// Type of backup - Full/Differential/Log + /// + public int BackupType { get; set; } + + /// + /// Backup device - Disk, Url, etc. + /// + public int BackupDeviceType { get; set; } + + /// + /// The text input of selected files + /// + public string SelectedFiles { get; set; } + + /// + /// Backupset name + /// + public string BackupsetName { get; set; } + + /// + /// List of selected file groups + /// + public Dictionary SelectedFileGroup { get; set; } + + /// + /// List of {key: backup path, value: device type} + /// + public Dictionary arChangesList { get; set; } + + /// + /// List of selected backup paths + /// + public ArrayList BackupPathList { get; set; } + } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/DisasterRecoveryConstants.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/DisasterRecoveryConstants.cs new file mode 100644 index 00000000..352a9ce1 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/DisasterRecoveryConstants.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery +{ + + public static class BackupConstants + { + public static string Backup = "Backup"; + + public static string RecoveryModelFull = "Full"; + public static string RecoveryModelSimple = "Simple"; + public static string RecoveryModelBulk = "BulkLogged"; + + public static string BackupTypeFull = "Full"; + public static string BackupTypeDiff = "Differential"; + public static string BackupTypeTLog = "Transaction Log"; + + public static string Database = "Database"; + public static string FileFilegroups = "File and Filegroups"; + public static string Filegroup = "Filegroup"; + public static string File = "File"; + } + + public static class RestoreConstants + { + public static string File = "File"; + public static string Url = "URL"; + + public static string Data = "Rows Data"; + public static string FileStream = "FILESTREAM Data"; + public static string Log = "Log"; + public static string FullText = "Full Text"; + public static string NotKnown = "Not known"; + + public static string TypeFull = "Full"; + public static string TypeTransactionLog = "Transaction Log"; + public static string TypeDifferential = "Differential"; + public static string TypeFilegroup = "Filegroup"; + public static string TypeFilegroupDifferential = "Filegroup Differential"; + public static string ComponentDatabase = "Database"; + public static string ComponentFile = "File"; + + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/DisasterRecoveryService.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/DisasterRecoveryService.cs index 7b070272..f0d9dca4 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/DisasterRecoveryService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/DisasterRecoveryService.cs @@ -4,23 +4,32 @@ // using Microsoft.SqlTools.Hosting.Protocol; +using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts; using Microsoft.SqlTools.ServiceLayer.Hosting; using Microsoft.SqlTools.ServiceLayer.SqlContext; using System; using System.Threading.Tasks; +using Microsoft.SqlServer.Management.Smo; +using Microsoft.SqlServer.Management.Common; +using System.Collections.Generic; +using System.Data.SqlClient; +using Microsoft.SqlTools.ServiceLayer.Common; namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery { public class DisasterRecoveryService { private static readonly Lazy instance = new Lazy(() => new DisasterRecoveryService()); + private static ConnectionService connectionService = null; + private BackupFactory backupFactory; /// /// Default, parameterless constructor. /// internal DisasterRecoveryService() { + this.backupFactory = new BackupFactory(); } /// @@ -31,10 +40,29 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery get { return instance.Value; } } + /// + /// Internal for testing purposes only + /// + internal static ConnectionService ConnectionServiceInstance + { + get + { + if (connectionService == null) + { + connectionService = ConnectionService.Instance; + } + return connectionService; + } + set + { + connectionService = value; + } + } + /// /// Initializes the service instance /// - public void InitializeService(ServiceHost serviceHost ) + public void InitializeService(ServiceHost serviceHost) { serviceHost.SetRequestHandler(BackupRequest.Type, HandleBackupRequest); } @@ -45,8 +73,89 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery internal static async Task HandleBackupRequest( BackupParams backupParams, RequestContext requestContext) - { + { + ConnectionInfo connInfo; + DisasterRecoveryService.ConnectionServiceInstance.TryFindConnection( + backupParams.OwnerUri, + out connInfo); + CDataContainer dataContainer; + + if (connInfo != null) + { + char[] passwordArray = connInfo.ConnectionDetails.Password.ToCharArray(); + if (string.Equals(connInfo.ConnectionDetails.AuthenticationType, "SqlLogin", StringComparison.OrdinalIgnoreCase)) + { + unsafe + { + fixed (char* passwordPtr = passwordArray) + { + dataContainer = new CDataContainer( + CDataContainer.ServerType.SQL, + connInfo.ConnectionDetails.ServerName, + false, + connInfo.ConnectionDetails.UserName, + new System.Security.SecureString(passwordPtr, passwordArray.Length), + string.Empty); + } + } + } + else + { + dataContainer = new CDataContainer( + CDataContainer.ServerType.SQL, + connInfo.ConnectionDetails.ServerName, + true, + null, + null, + null); + } + + SqlConnection sqlConn = GetSqlConnection(connInfo); + if (sqlConn != null) + { + DisasterRecoveryService.Instance.InitializeBackup(dataContainer, sqlConn, backupParams.BackupInfo); + DisasterRecoveryService.Instance.PerformBackup(); + } + } + await requestContext.SendResult(new BackupResponse()); } + + internal static SqlConnection GetSqlConnection(ConnectionInfo connInfo) + { + try + { + // increase the connection timeout to at least 30 seconds and and build connection string + // enable PersistSecurityInfo to handle issues in SMO where the connection context is lost in reconnections + int? originalTimeout = connInfo.ConnectionDetails.ConnectTimeout; + bool? originalPersistSecurityInfo = connInfo.ConnectionDetails.PersistSecurityInfo; + connInfo.ConnectionDetails.ConnectTimeout = Math.Max(30, originalTimeout ?? 0); + connInfo.ConnectionDetails.PersistSecurityInfo = true; + string connectionString = ConnectionService.BuildConnectionString(connInfo.ConnectionDetails); + connInfo.ConnectionDetails.ConnectTimeout = originalTimeout; + connInfo.ConnectionDetails.PersistSecurityInfo = originalPersistSecurityInfo; + + // open a dedicated binding server connection + SqlConnection sqlConn = new SqlConnection(connectionString); + sqlConn.Open(); + return sqlConn; + } + catch (Exception) + { + } + + return null; + } + + private void InitializeBackup(CDataContainer dataContainer, SqlConnection sqlConnection, BackupInfo input) + { + this.backupFactory.Initialize(dataContainer, sqlConnection, input); + } + + private void PerformBackup() + { + this.backupFactory.PerformBackup(); + } + } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/UrlControl.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/UrlControl.cs new file mode 100644 index 00000000..08587a9c --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/UrlControl.cs @@ -0,0 +1,34 @@ +using System.Collections; + +namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery +{ + //TODO: Should URL be added to Carbon? + public partial class UrlControl + { + /// + /// Server + /// + public Microsoft.SqlServer.Management.Smo.Server SqlServer; + + /// + /// list of Backup Urls + /// + private ArrayList listBakDestUrls; + + public UrlControl() + { + } + + /// + /// List of backup urls + /// + public ArrayList ListBakDestUrls + { + get + { + return this.listBakDestUrls; + } + } + + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/project.json b/src/Microsoft.SqlTools.ServiceLayer/project.json index 6277d535..e9a57e12 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/project.json +++ b/src/Microsoft.SqlTools.ServiceLayer/project.json @@ -43,7 +43,7 @@ }, "Microsoft.SqlTools.Credentials": { "target": "project" - } + } }, "frameworks": { "netcoreapp1.0": {