mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 10:58:30 -05:00
Add support for Dedicated Administrator Connection (#466)
* Prototype support for admin connection * Added test and used correct default for ADMIN connection casing
This commit is contained in:
@@ -29,6 +29,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ConnectionService
|
public class ConnectionService
|
||||||
{
|
{
|
||||||
|
public const string AdminConnectionPrefix = "ADMIN:";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Singleton service instance
|
/// Singleton service instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -444,6 +446,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
|||||||
throw new InvalidOperationException(SR.ConnectionServiceDbErrorDefaultNotConnected(ownerUri));
|
throw new InvalidOperationException(SR.ConnectionServiceDbErrorDefaultNotConnected(ownerUri));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(IsDedicatedAdminConnection(connectionInfo.ConnectionDetails))
|
||||||
|
{
|
||||||
|
// Since this is a dedicated connection only 1 is allowed at any time. Return the default connection for use in the requested action
|
||||||
|
return defaultConnection;
|
||||||
|
}
|
||||||
|
|
||||||
// Try to get the DbConnection
|
// Try to get the DbConnection
|
||||||
DbConnection connection;
|
DbConnection connection;
|
||||||
if (!connectionInfo.TryGetConnection(connectionType, out connection) && ConnectionType.Default != connectionType)
|
if (!connectionInfo.TryGetConnection(connectionType, out connection) && ConnectionType.Default != connectionType)
|
||||||
@@ -842,12 +850,33 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
|||||||
await requestContext.SendError(ex.ToString());
|
await requestContext.SendError(ex.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a ConnectionDetails object represents a DAC connection
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connectionDetails"></param>
|
||||||
|
public static bool IsDedicatedAdminConnection(ConnectionDetails connectionDetails)
|
||||||
|
{
|
||||||
|
Validate.IsNotNull(nameof(connectionDetails), connectionDetails);
|
||||||
|
SqlConnectionStringBuilder builder = CreateConnectionStringBuilder(connectionDetails);
|
||||||
|
string serverName = builder.DataSource;
|
||||||
|
return serverName != null && serverName.StartsWith(AdminConnectionPrefix, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Build a connection string from a connection details instance
|
/// Build a connection string from a connection details instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="connectionDetails"></param>
|
/// <param name="connectionDetails"></param>
|
||||||
public static string BuildConnectionString(ConnectionDetails connectionDetails)
|
public static string BuildConnectionString(ConnectionDetails connectionDetails)
|
||||||
|
{
|
||||||
|
return CreateConnectionStringBuilder(connectionDetails).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Build a connection string builder a connection details instance
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connectionDetails"></param>
|
||||||
|
public static SqlConnectionStringBuilder CreateConnectionStringBuilder(ConnectionDetails connectionDetails)
|
||||||
{
|
{
|
||||||
SqlConnectionStringBuilder connectionBuilder;
|
SqlConnectionStringBuilder connectionBuilder;
|
||||||
|
|
||||||
@@ -981,7 +1010,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
|||||||
connectionBuilder.TypeSystemVersion = connectionDetails.TypeSystemVersion;
|
connectionBuilder.TypeSystemVersion = connectionDetails.TypeSystemVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
return connectionBuilder.ToString();
|
return connectionBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -834,6 +834,11 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
{
|
{
|
||||||
await Task.Run(() =>
|
await Task.Run(() =>
|
||||||
{
|
{
|
||||||
|
if (ConnectionService.IsDedicatedAdminConnection(info.ConnectionDetails))
|
||||||
|
{
|
||||||
|
// Intellisense cannot be run on these connections as only 1 SqlConnection can be opened on them at a time
|
||||||
|
return;
|
||||||
|
}
|
||||||
ScriptParseInfo scriptInfo = GetScriptParseInfo(info.OwnerUri, createIfNotExists: true);
|
ScriptParseInfo scriptInfo = GetScriptParseInfo(info.OwnerUri, createIfNotExists: true);
|
||||||
if (Monitor.TryEnter(scriptInfo.BuildingMetadataLock, LanguageService.OnConnectionWaitTimeout))
|
if (Monitor.TryEnter(scriptInfo.BuildingMetadataLock, LanguageService.OnConnectionWaitTimeout))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1210,6 +1210,48 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Connection
|
|||||||
await Assert.ThrowsAsync<InvalidOperationException>(
|
await Assert.ThrowsAsync<InvalidOperationException>(
|
||||||
() => service.GetOrOpenConnection(TestObjects.ScriptUri, ConnectionType.Query));
|
() => service.GetOrOpenConnection(TestObjects.ScriptUri, ConnectionType.Query));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetOrOpenAdminDefaultConnection()
|
||||||
|
{
|
||||||
|
// Setup: Create a connection service with an empty connection info obj
|
||||||
|
var service = TestObjects.GetTestConnectionService();
|
||||||
|
var connInfo = new ConnectionInfo(null, null, null);
|
||||||
|
service.OwnerToConnectionMap[TestObjects.ScriptUri] = connInfo;
|
||||||
|
|
||||||
|
// If: I ask for a connection on a connection that doesn't have a default connection
|
||||||
|
// Then: An exception should be thrown
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(
|
||||||
|
() => service.GetOrOpenConnection(TestObjects.ScriptUri, ConnectionType.Query));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ConnectionWithAdminConnectionEnsuresOnlyOneConnectionCreated()
|
||||||
|
{
|
||||||
|
// If I try to connect using a connection string, it overrides the server name and username for the connection
|
||||||
|
ConnectParams connectionParameters = TestObjects.GetTestConnectionParams();
|
||||||
|
string serverName = "ADMIN:overriddenServerName";
|
||||||
|
string userName = "overriddenUserName";
|
||||||
|
connectionParameters.Connection.ServerName = serverName;
|
||||||
|
connectionParameters.Connection.UserName = userName;
|
||||||
|
|
||||||
|
// Connect
|
||||||
|
ConnectionService service = TestObjects.GetTestConnectionService();
|
||||||
|
var connectionResult = await service.Connect(connectionParameters);
|
||||||
|
|
||||||
|
// Verify you can get the connection for default
|
||||||
|
DbConnection defaultConn = await service.GetOrOpenConnection(connectionParameters.OwnerUri, ConnectionType.Default);
|
||||||
|
ConnectionInfo connInfo = service.OwnerToConnectionMap[connectionParameters.OwnerUri];
|
||||||
|
Assert.NotNull(defaultConn);
|
||||||
|
Assert.Equal(connInfo.AllConnections.Count, 1);
|
||||||
|
|
||||||
|
// Verify that for the Query, no new connection is created
|
||||||
|
DbConnection queryConn = await service.GetOrOpenConnection(connectionParameters.OwnerUri, ConnectionType.Query);
|
||||||
|
connInfo = service.OwnerToConnectionMap[connectionParameters.OwnerUri];
|
||||||
|
Assert.NotNull(defaultConn);
|
||||||
|
Assert.Equal(connInfo.AllConnections.Count, 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ConnectionWithConnectionStringSucceeds()
|
public async Task ConnectionWithConnectionStringSucceeds()
|
||||||
|
|||||||
Reference in New Issue
Block a user