From adc13cff8209981f7066ecc8aa3c1e4554d5ea8d Mon Sep 17 00:00:00 2001 From: Yurong He <43652751+YurongHe@users.noreply.github.com> Date: Thu, 24 Jan 2019 20:06:35 -0800 Subject: [PATCH] Unified connection support (#765) * Added the query to get big data cluster endpoints Added bigDataClusterEndpoints to ObjectExplorerSession Added bigDataClusterEndpoints to ServerInfo Fixes some tests. * Removed bigDataClusterEndpoints from session * Since server property to get IsBigDataCluster is not implemented yet, use query of sys.asseblies instead to unblock us. Need to use server property when it is available Add options to ServerInfo, so no need to change the contract in the further when adding new properties. Will move the existing properties to option later * Undo changes not needed * Resolved PR comments. * Fixed node is null exception when can't find NodePath. * Added comments * Removed not used using * Catch sqlException for the reader of BDC endpoints and set empty list to the option. * Added comments for returning empty nodes. --- .../Connection/ConnectionService.cs | 3 +- .../Connection/Contracts/ServerInfo.cs | 15 +++++++ .../ReliableConnectionHelper.cs | 41 ++++++++++++++++++- .../SqlConnectionHelperScripts.cs | 5 ++- .../ObjectExplorer/ObjectExplorerService.cs | 16 +++++++- 5 files changed, 74 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs index 9f626720..b0080b75 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs @@ -440,7 +440,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection IsCloud = serverInfo.IsCloud, AzureVersion = serverInfo.AzureVersion, OsVersion = serverInfo.OsVersion, - MachineName = serverInfo.MachineName + MachineName = serverInfo.MachineName, + Options = serverInfo.Options, }; connectionInfo.IsCloud = serverInfo.IsCloud; connectionInfo.MajorVersion = serverInfo.ServerMajorVersion; diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ServerInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ServerInfo.cs index 72305180..a62ff495 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ServerInfo.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ServerInfo.cs @@ -3,6 +3,9 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using System; +using System.Collections.Generic; + namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts { /// @@ -64,5 +67,17 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts /// The Operating System version string of the machine running the SQL Server instance. /// public string MachineName { get; set; } + + /// + /// Server options + /// + public Dictionary Options { get; set; } } } + +public class ClusterEndpoint +{ + public string ServiceName; + public string IpAddress; + public int Port; +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/ReliableConnection/ReliableConnectionHelper.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/ReliableConnection/ReliableConnectionHelper.cs index 77c055e5..18441aa7 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Connection/ReliableConnection/ReliableConnectionHelper.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/ReliableConnection/ReliableConnectionHelper.cs @@ -29,7 +29,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection private const string DacFxApplicationName = "DacFx"; private const int SqlDwEngineEditionId = (int)DatabaseEngineEdition.SqlDataWarehouse; - + // See MSDN documentation for "SERVERPROPERTY (SQL Azure Database)" for "EngineEdition" property: // http://msdn.microsoft.com/en-us/library/ee336261.aspx private const int SqlAzureEngineEditionId = 5; @@ -681,6 +681,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection public class ServerInfo { + internal const string OptionIsBigDataCluster = "isBigDataCluster"; + internal const string OptionClusterEndpoints = "clusterEndpoints"; public int ServerMajorVersion; public int ServerMinorVersion; public int ServerReleaseVersion; @@ -699,6 +701,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection public string OsVersion; public string MachineName; + + public Dictionary Options { get; set; } } public static bool TryGetServerVersion(string connectionString, out ServerInfo serverInfo, string azureAccountToken) @@ -754,6 +758,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection delegate (IDataReader reader) { reader.Read(); + int engineEditionId = Int32.Parse(reader[0].ToString(), CultureInfo.InvariantCulture); serverInfo.EngineEditionId = engineEditionId; @@ -770,6 +775,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection serverInfo.IsSelectiveXmlIndexMetadataPresent = reader.GetInt32(5) == 1; } + if (reader.FieldCount > 6) + { + serverInfo.Options = new Dictionary(); + serverInfo.Options.Add(ServerInfo.OptionIsBigDataCluster, reader.GetInt32(6)); + } + // The 'ProductVersion' server property is of the form ##.#[#].####.#, Version serverVersion = new Version(serverInfo.ServerVersion); @@ -806,6 +817,34 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection serverInfo.OsVersion = reader[0].ToString(); }); + // Get BDC endpoints + if (serverInfo != null && serverInfo.Options != null && serverInfo.Options.ContainsKey(ServerInfo.OptionIsBigDataCluster)) + { + int? isBigDataCluster = serverInfo.Options[ServerInfo.OptionIsBigDataCluster] as int?; + List clusterEndpoints = new List(); + try + { + if (isBigDataCluster != null && isBigDataCluster.Value == 1) + { + ExecuteReader( + connection, + SqlConnectionHelperScripts.GetClusterEndpoints, + delegate (IDataReader reader) + { + while (reader.Read()) + { + clusterEndpoints.Add(new ClusterEndpoint { ServiceName = reader.GetString(0), IpAddress = reader.GetString(1), Port = reader.GetInt32(2) }); + } + serverInfo.Options.Add(ServerInfo.OptionClusterEndpoints, clusterEndpoints); + }); + } + } + catch (SqlException) + { + serverInfo.Options.Add(ServerInfo.OptionClusterEndpoints, clusterEndpoints); + } + } + return serverInfo; }; diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/ReliableConnection/SqlConnectionHelperScripts.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/ReliableConnection/SqlConnectionHelperScripts.cs index 588b348f..247dd196 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Connection/ReliableConnection/SqlConnectionHelperScripts.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/ReliableConnection/SqlConnectionHelperScripts.cs @@ -7,8 +7,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection { static class SqlConnectionHelperScripts { - public const string EngineEdition = "SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT)"; - public const string EngineEditionWithLock = "SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT)"; + public const string EngineEdition = "SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT), (SELECT CASE WHEN EXISTS (SELECT name FROM model.sys.assemblies WHERE name = 'Microsoft.SqlServer.DataPoolManagement.Assemblies') THEN 1 ELSE 0 END)"; + public const string EngineEditionWithLock = "SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT), (SELECT CASE WHEN EXISTS (SELECT name FROM model.sys.assemblies WHERE name = 'Microsoft.SqlServer.DataPoolManagement.Assemblies') THEN 1 ELSE 0 END)"; public const string CheckDatabaseReadonly = @"EXEC sp_dboption '{0}', 'read only'"; @@ -47,5 +47,6 @@ SELECT @filepath AS FilePath "; public const string GetOsVersion = @"SELECT OSVersion = RIGHT(@@version, LEN(@@version)- 3 -charindex (' ON ', @@version))"; + public const string GetClusterEndpoints = @"SELECT [service_name], [ip_address], [port] FROM [master].[dbo].[cluster_endpoint_info]"; } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs index 36776550..4c773fa8 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs @@ -386,7 +386,6 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer RootNode = session.Root.ToNodeInfo(), SessionId = uri, ErrorMessage = session.ErrorMessage - }; await serviceHost.SendEvent(CreateSessionCompleteNotification.Type, response); return response; @@ -406,7 +405,20 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer { NodeInfo[] nodes = null; TreeNode node = session.Root.FindNodeByPath(nodePath); - ExpandResponse response = new ExpandResponse { Nodes = new NodeInfo[] { }, ErrorMessage = node.ErrorMessage, SessionId = session.Uri, NodePath = nodePath }; + ExpandResponse response = null; + + // This node was likely returned from a different node provider. Ignore expansion and return an empty array + // since we don't need to add any nodes under this section of the tree. + if (node == null) + { + response = new ExpandResponse { Nodes = new NodeInfo[] { }, ErrorMessage = string.Empty, SessionId = session.Uri, NodePath = nodePath }; + response.Nodes = new NodeInfo[0]; + return response; + } + else + { + response = new ExpandResponse { Nodes = new NodeInfo[] { }, ErrorMessage = node.ErrorMessage, SessionId = session.Uri, NodePath = nodePath }; + } if (node != null && Monitor.TryEnter(node.BuildingMetadataLock, LanguageService.OnConnectionWaitTimeout)) {