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": {