diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionDetails.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionDetails.cs index 0c6397f6..ccaff015 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionDetails.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionDetails.cs @@ -586,6 +586,21 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts } } + /// + /// Gets or sets the connection name + /// + public string ConnectionName + { + get + { + return GetOptionValue("connectionName"); + } + set + { + SetOptionValue("connectionName", value); + } + } + /// /// Gets or sets the database display name /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/ConnectedBindingQueue.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/ConnectedBindingQueue.cs index 2e7b30f3..3b8905e3 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/ConnectedBindingQueue.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/ConnectedBindingQueue.cs @@ -6,6 +6,7 @@ #nullable disable using System; +using System.Collections.Generic; using Microsoft.SqlServer.Management.Common; using Microsoft.SqlServer.Management.SmoMetadataProvider; using Microsoft.SqlServer.Management.SqlParser.Binder; @@ -70,7 +71,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices public ConnectedBindingQueue() : this(true) - { + { } public ConnectedBindingQueue(bool needsMetadata) @@ -90,7 +91,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices /// /// internal static string GetConnectionContextKey(ConnectionDetails details) - { + { string key = string.Format("{0}_{1}_{2}_{3}", details.ServerName ?? "NULL", details.DatabaseName ?? "NULL", @@ -108,6 +109,39 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices key += "_" + details.GroupId; } + if (!string.IsNullOrEmpty(details.ConnectionName)) + { + key += "_" + details.ConnectionName; + } + + // Additional properties that are used to distinguish the connection (besides password) + // These are so that multiple connections can connect to the same target, with different settings. + foreach (KeyValuePair entry in details.Options) + { + // Filter out properties we already have or don't want (password) + if (entry.Key != "server" && entry.Key != "database" && entry.Key != "user" + && entry.Key != "authenticationType" && entry.Key != "databaseDisplayName" + && entry.Key != "groupId" && entry.Key != "password" && entry.Key != "connectionName") + { + // Boolean values are explicitly labeled true or false instead of undefined. + if (entry.Value is bool) + { + if ((bool)entry.Value) + { + key += "_" + entry.Key + ":true"; + } + else + { + key += "_" + entry.Key + ":false"; + } + } + else if (!string.IsNullOrEmpty(entry.Value as String)) + { + key += "_" + entry.Key + ":" + entry.Value; + } + } + } + return Uri.EscapeUriString(key); } @@ -120,7 +154,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices return string.Format("{0}_{1}", serverName ?? "NULL", databaseName ?? "NULL"); - + } public void CloseConnections(string serverName, string databaseName, int millisecondsTimeout) @@ -198,7 +232,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices try { bindingContext.BindingLock.Reset(); - + // populate the binding context to work with the SMO metadata provider bindingContext.ServerConnection = connectionOpener.OpenServerConnection(connInfo, featureName); @@ -210,19 +244,19 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices this.CurrentSettings.SqlTools.IntelliSense.LowerCaseSuggestions.Value ? CasingStyle.Lowercase : CasingStyle.Uppercase; bindingContext.Binder = BinderProvider.CreateBinder(bindingContext.SmoMetadataProvider); - } - + } + bindingContext.BindingTimeout = ConnectedBindingQueue.DefaultBindingTimeout; bindingContext.IsConnected = true; } catch (Exception) { bindingContext.IsConnected = false; - } + } finally { bindingContext.BindingLock.Set(); - } + } } return connectionKey; diff --git a/src/Microsoft.SqlTools.ServiceLayer/TableDesigner/TableDesignerService.cs b/src/Microsoft.SqlTools.ServiceLayer/TableDesigner/TableDesignerService.cs index 84b5b133..de31b69b 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/TableDesigner/TableDesignerService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/TableDesigner/TableDesignerService.cs @@ -1830,9 +1830,11 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner private void UpdateTableTitleInfo(TableInfo tableInfo) { var td = GetTableDesigner(tableInfo); + var advancedOpsIndex = tableInfo.Tooltip.LastIndexOf('['); + var advancedOps = tableInfo.Tooltip.Substring(advancedOpsIndex); tableInfo.Title = td.TableViewModel.FullName; var tableParent = tableInfo.Server == null ? tableInfo.ProjectFilePath : string.Format("{0} - {1}", tableInfo.Server, tableInfo.Database); - tableInfo.Tooltip = string.Format("{0} - {1}", tableParent, tableInfo.Title); + tableInfo.Tooltip = string.Format("{0} - {1} {2}", tableParent, tableInfo.Title, advancedOps); } private Dictionary GetMetadata(TableInfo tableInfo) diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/ObjectExplorer/ObjectExplorerServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/ObjectExplorer/ObjectExplorerServiceTests.cs index 2ebd0198..d340b16a 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/ObjectExplorer/ObjectExplorerServiceTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/ObjectExplorer/ObjectExplorerServiceTests.cs @@ -56,7 +56,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer ConnectedBindingContext connectedBindingContext = new ConnectedBindingContext(); connectedBindingContext.ServerConnection = new ServerConnection(new SqlConnection(fakeConnectionString)); connectedBindingQueue = new ConnectedBindingQueue(false); - connectedBindingQueue.BindingContextMap.Add($"{details.ServerName}_{details.DatabaseName}_{details.UserName}_NULL", connectedBindingContext); + connectedBindingQueue.BindingContextMap.Add($"{details.ServerName}_{details.DatabaseName}_{details.UserName}_NULL_persistSecurityInfo:true", connectedBindingContext); connectedBindingQueue.BindingContextTasks.Add(connectedBindingContext, Task.Run(() => null)); mockConnectionOpener = new Mock(); connectedBindingQueue.SetConnectionOpener(mockConnectionOpener.Object);