From 14f5a3e0f129d89e15147e7e9cd5bf5da6c6826a Mon Sep 17 00:00:00 2001
From: Justin M <63619224+JustinMDotNet@users.noreply.github.com>
Date: Mon, 24 Aug 2020 13:18:00 -0700
Subject: [PATCH] 3278 Kusto Unit Test Refactor (#1053)
* 3278 Moved functions related to Metadata from DataSourceFactory to MetadataFactory.cs
* 3278 Refactored DataSourceFactory to not be static. Added IDataSourceFactory interface.
* 3278 Refactored IKustoIntellisenseHelper.cs to not be static. Added IKustoIntellisenseHelper.cs interface.
* 3278 Removed unused functions from Scripter and deleted ScripterCore.cs because it was unused. Refactored Scripter.cs methods to not be static and created IScripter.cs
* 3278 Refactored datsasourceConnectionFactory in ConnectionService to use MEF through the InitializeService function
* 3278 Refactored IAutoCompleteHelper to not be static and added IAutoCompleteHelper interface.
* 3278 Removed unused classes DatabaseFullAccessException and FeatureWithFullDbAccess. Refactored ObjectExplorerService to use ImportingConstructor attribute. Removed commented out in KustoResultsReader. Removed unused functions from DatabaseLocksManager. Removed unused IDataSourceConnectionFactory from ConnectionService
* 3278 Moved SqlConnectionOpener class to new file. Added new interfaces IConnectedBindingQueue.cs and ISqlConnectionOpener.cs Added sqlConnectionOpener to dependency in ConnectedBindingQueue.
* 3278 Removed needsMetadata param from ConnectedBindingQueue. Added param to AddConnectionContext where it's used
* 3278 Refactored ConnectedBindingQueue to use MEF. Refactored usages to run against interface.
* 3278 Corrected ServiceHost namespace. Removed unused dependency to sqlToolsContext in LanguageService. Updated dependency in MetadataService and LanguageService to IProtocolEndpoint instead of ServiceHost
* 3278 Added back NextResult function and summary to KustoResultsReader. Renamed instance to _instance in ConnectionService and DatabaseLocksManager to stay consistent. Changed OpenDataSourceConnection to private in ConnectionService
* 3278 Converted helper methods back to static and removed backing interfaces
* 3278 Reverted AdminService > InitializeService to use ServiceHost as param
---
.../Admin/AdminService.cs | 16 +-
.../Connection/ConnectionService.cs | 60 +-
.../Connection/DataSourceConnectionFactory.cs | 5 +-
.../Connection/DatabaseFullAccessException.cs | 28 -
.../Connection/DatabaseLocksManager.cs | 86 +--
.../Connection/FeatureWithFullDbAccess.cs | 40 -
.../DataSource/DataReaderWrapper.cs | 4 +-
.../DataSource/DataSourceFactory.cs | 95 +--
.../KustoIntellisenseHelper.cs | 5 +-
.../DataSource/KustoDataSource.cs | 17 +-
.../DataSource/KustoResultsReader.cs | 19 +-
.../DataSource/Metadata/MetadataFactory.cs | 104 +++
.../ReliableDataSourceConnection.cs | 16 +-
.../HostLoader.cs | 15 +-
.../LanguageServices/AutoCompleteHelper.cs | 3 +-
.../LanguageServices/ConnectedBindingQueue.cs | 81 +--
.../IConnectedBindingQueue.cs | 26 +
.../LanguageServices/ISqlConnectionOpener.cs | 13 +
.../LanguageServices/LanguageService.cs | 63 +-
.../LanguageServices/SqlConnectionOpener.cs | 15 +
.../Metadata/MetadataService.cs | 14 +-
.../ObjectExplorer/ObjectExplorerService.cs | 86 +--
src/Microsoft.Kusto.ServiceLayer/Program.cs | 3 -
.../QueryExecution/QueryExecutionService.cs | 1 -
.../Scripting/IScripter.cs | 12 +
.../Scripting/ScriptAsScriptingOperation.cs | 19 +-
.../Scripting/Scripter.cs | 81 +--
.../Scripting/ScripterCore.cs | 683 ------------------
.../Scripting/ScriptingScriptOperation.cs | 3 +-
.../Scripting/ScriptingService.cs | 13 +-
.../Scripting/SmoScriptingOperation.cs | 6 +-
.../ServiceHost.cs | 8 +-
.../Workspace/WorkspaceService.cs | 1 -
.../ObjectExplorer/ObjectExplorerService.cs | 9 -
34 files changed, 355 insertions(+), 1295 deletions(-)
delete mode 100644 src/Microsoft.Kusto.ServiceLayer/Connection/DatabaseFullAccessException.cs
delete mode 100644 src/Microsoft.Kusto.ServiceLayer/Connection/FeatureWithFullDbAccess.cs
create mode 100644 src/Microsoft.Kusto.ServiceLayer/DataSource/Metadata/MetadataFactory.cs
create mode 100644 src/Microsoft.Kusto.ServiceLayer/LanguageServices/IConnectedBindingQueue.cs
create mode 100644 src/Microsoft.Kusto.ServiceLayer/LanguageServices/ISqlConnectionOpener.cs
create mode 100644 src/Microsoft.Kusto.ServiceLayer/LanguageServices/SqlConnectionOpener.cs
create mode 100644 src/Microsoft.Kusto.ServiceLayer/Scripting/IScripter.cs
delete mode 100644 src/Microsoft.Kusto.ServiceLayer/Scripting/ScripterCore.cs
diff --git a/src/Microsoft.Kusto.ServiceLayer/Admin/AdminService.cs b/src/Microsoft.Kusto.ServiceLayer/Admin/AdminService.cs
index 3c86dbbd..dfcadf16 100644
--- a/src/Microsoft.Kusto.ServiceLayer/Admin/AdminService.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/Admin/AdminService.cs
@@ -12,7 +12,6 @@ using Microsoft.Kusto.ServiceLayer.Admin.Contracts;
using Microsoft.Kusto.ServiceLayer.Connection;
using Microsoft.Kusto.ServiceLayer.DataSource;
using Microsoft.Kusto.ServiceLayer.DataSource.Metadata;
-using Microsoft.Kusto.ServiceLayer.Hosting;
using Microsoft.Kusto.ServiceLayer.Utility;
namespace Microsoft.Kusto.ServiceLayer.Admin
@@ -26,13 +25,6 @@ namespace Microsoft.Kusto.ServiceLayer.Admin
private static ConnectionService connectionService = null;
- ///
- /// Default, parameterless constructor.
- ///
- internal AdminService()
- {
- }
-
///
/// Internal for testing purposes only
///
@@ -72,7 +64,7 @@ namespace Microsoft.Kusto.ServiceLayer.Admin
///
/// Handle get database info request
///
- internal static async Task HandleGetDatabaseInfoRequest(
+ internal async Task HandleGetDatabaseInfoRequest(
GetDatabaseInfoParams databaseParams,
RequestContext requestContext)
{
@@ -114,18 +106,18 @@ namespace Microsoft.Kusto.ServiceLayer.Admin
///
///
///
- internal static DatabaseInfo GetDatabaseInfo(ConnectionInfo connInfo)
+ internal DatabaseInfo GetDatabaseInfo(ConnectionInfo connInfo)
{
if(!string.IsNullOrEmpty(connInfo.ConnectionDetails.DatabaseName)){
ReliableDataSourceConnection connection;
connInfo.TryGetConnection("Default", out connection);
IDataSource dataSource = connection.GetUnderlyingConnection();
- DataSourceObjectMetadata objectMetadata = DataSourceFactory.CreateClusterMetadata(connInfo.ConnectionDetails.ServerName);
+ DataSourceObjectMetadata objectMetadata = MetadataFactory.CreateClusterMetadata(connInfo.ConnectionDetails.ServerName);
List metadata = dataSource.GetChildObjects(objectMetadata, true).ToList();
var databaseMetadata = metadata.Where(o => o.Name == connInfo.ConnectionDetails.DatabaseName);
- List databaseInfo = DataSourceFactory.ConvertToDatabaseInfo(databaseMetadata);
+ List databaseInfo = MetadataFactory.ConvertToDatabaseInfo(databaseMetadata);
return databaseInfo.ElementAtOrDefault(0);
}
diff --git a/src/Microsoft.Kusto.ServiceLayer/Connection/ConnectionService.cs b/src/Microsoft.Kusto.ServiceLayer/Connection/ConnectionService.cs
index da287c1d..f89915b7 100644
--- a/src/Microsoft.Kusto.ServiceLayer/Connection/ConnectionService.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/Connection/ConnectionService.cs
@@ -38,18 +38,13 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
///
/// Singleton service instance
///
- private static readonly Lazy instance
+ private static readonly Lazy _instance
= new Lazy(() => new ConnectionService());
///
/// Gets the singleton service instance
///
- public static ConnectionService Instance => instance.Value;
-
- ///
- /// The SQL connection factory object
- ///
- private IDataSourceConnectionFactory connectionFactory;
+ public static ConnectionService Instance => _instance.Value;
private DatabaseLocksManager lockedDatabaseManager;
@@ -113,9 +108,6 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
///
public ConnectionService()
{
- var defaultQueue = new ConnectedBindingQueue(needsMetadata: false);
- connectedQueues.AddOrUpdate("Default", defaultQueue, (key, old) => defaultQueue);
- this.LockedDatabaseManager.ConnectionService = this;
}
///
@@ -181,25 +173,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
///
/// Gets the SQL connection factory instance
///
- public IDataSourceConnectionFactory ConnectionFactory
- {
- get
- {
- if (this.connectionFactory == null)
- {
- this.connectionFactory = new DataSourceConnectionFactory();
- }
- return this.connectionFactory;
- }
-
- internal set { this.connectionFactory = value; }
- }
-
- ///
- /// Test constructor that injects dependency interfaces
- ///
- ///
- public ConnectionService(IDataSourceConnectionFactory testFactory) => this.connectionFactory = testFactory;
+ private IDataSourceConnectionFactory _dataSourceConnectionFactory;
// Attempts to link a URI to an actively used connection for this URI
public virtual bool TryFindConnection(string ownerUri, out ConnectionInfo connectionInfo) => this.OwnerToConnectionMap.TryGetValue(ownerUri, out connectionInfo);
@@ -254,7 +228,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
bool connectionChanged = false;
if (!OwnerToConnectionMap.TryGetValue(connectionParams.OwnerUri, out connectionInfo))
{
- connectionInfo = new ConnectionInfo(ConnectionFactory, connectionParams.OwnerUri, connectionParams.Connection);
+ connectionInfo = new ConnectionInfo(_dataSourceConnectionFactory, connectionParams.OwnerUri, connectionParams.Connection);
}
else if (IsConnectionChanged(connectionParams, connectionInfo))
{
@@ -267,7 +241,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
if (connectionChanged)
{
- connectionInfo = new ConnectionInfo(ConnectionFactory, connectionParams.OwnerUri, connectionParams.Connection);
+ connectionInfo = new ConnectionInfo(_dataSourceConnectionFactory, connectionParams.OwnerUri, connectionParams.Connection);
}
// Try to open a connection with the given ConnectParams
@@ -362,11 +336,6 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
return false;
}
- private bool IsDefaultConnectionType(string connectionType)
- {
- return string.IsNullOrEmpty(connectionType) || ConnectionType.Default.Equals(connectionType, StringComparison.CurrentCultureIgnoreCase);
- }
-
private void DisconnectExistingConnectionIfNeeded(ConnectParams connectionParams, ConnectionInfo connectionInfo, bool disconnectAll)
{
// Resolve if it is an existing connection
@@ -425,7 +394,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
var reliableConnection = connection as ReliableDataSourceConnection;
IDataSource dataSource = reliableConnection.GetUnderlyingConnection();
- DataSourceObjectMetadata clusterMetadata = DataSourceFactory.CreateClusterMetadata(connectionInfo.ConnectionDetails.ServerName);
+ DataSourceObjectMetadata clusterMetadata = MetadataFactory.CreateClusterMetadata(connectionInfo.ConnectionDetails.ServerName);
DiagnosticsInfo clusterDiagnostics = dataSource.GetDiagnostics(clusterMetadata);
ReliableConnectionHelper.ServerInfo serverInfo = DataSourceFactory.ConvertToServerinfoFormat(DataSourceType.Kusto, clusterDiagnostics);
@@ -801,14 +770,14 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
ConnectionDetails connectionDetails = info.ConnectionDetails.Clone();
IDataSource dataSource = OpenDataSourceConnection(info);
- DataSourceObjectMetadata objectMetadata = DataSourceFactory.CreateClusterMetadata(info.ConnectionDetails.ServerName);
+ DataSourceObjectMetadata objectMetadata = MetadataFactory.CreateClusterMetadata(info.ConnectionDetails.ServerName);
ListDatabasesResponse response = new ListDatabasesResponse();
// Mainly used by "manage" dashboard
if(listDatabasesParams.IncludeDetails.HasTrue()){
IEnumerable databaseMetadataInfo = dataSource.GetChildObjects(objectMetadata, true);
- List metadata = DataSourceFactory.ConvertToDatabaseInfo(databaseMetadataInfo);
+ List metadata = MetadataFactory.ConvertToDatabaseInfo(databaseMetadataInfo);
response.Databases = metadata.ToArray();
return response;
@@ -819,9 +788,14 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
return response;
}
- public void InitializeService(IProtocolEndpoint serviceHost)
+ public void InitializeService(IProtocolEndpoint serviceHost, IDataSourceConnectionFactory dataSourceConnectionFactory,
+ IConnectedBindingQueue connectedBindingQueue)
{
- this.ServiceHost = serviceHost;
+ ServiceHost = serviceHost;
+ _dataSourceConnectionFactory = dataSourceConnectionFactory;
+
+ connectedQueues.AddOrUpdate("Default", connectedBindingQueue, (key, old) => connectedBindingQueue);
+ LockedDatabaseManager.ConnectionService = this;
// Register request and event handlers with the Service Host
serviceHost.SetRequestHandler(ConnectionRequest.Type, HandleConnectRequest);
@@ -1429,12 +1403,12 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
/// The connection info to connect with
/// A plaintext string that will be included in the application name for the connection
/// A SqlConnection created with the given connection info
- internal static IDataSource OpenDataSourceConnection(ConnectionInfo connInfo, string featureName = null)
+ private IDataSource OpenDataSourceConnection(ConnectionInfo connInfo, string featureName = null)
{
try
{
// generate connection string
- string connectionString = ConnectionService.BuildConnectionString(connInfo.ConnectionDetails);
+ string connectionString = BuildConnectionString(connInfo.ConnectionDetails);
// TODOKusto: Pass in type of DataSource needed to make this generic. Hard coded to Kusto right now.
return DataSourceFactory.Create(DataSourceType.Kusto, connectionString, connInfo.ConnectionDetails.AzureAccountToken);
diff --git a/src/Microsoft.Kusto.ServiceLayer/Connection/DataSourceConnectionFactory.cs b/src/Microsoft.Kusto.ServiceLayer/Connection/DataSourceConnectionFactory.cs
index c676decd..03dac2f4 100644
--- a/src/Microsoft.Kusto.ServiceLayer/Connection/DataSourceConnectionFactory.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/Connection/DataSourceConnectionFactory.cs
@@ -3,8 +3,8 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
-using System.Data.Common;
-using Microsoft.Kusto.ServiceLayer.Connection;
+using System.Composition;
+using Microsoft.Kusto.ServiceLayer.DataSource;
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
namespace Microsoft.Kusto.ServiceLayer.Connection
@@ -14,6 +14,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
/// The purpose of the factory is to make it easier to mock out the database
/// in 'offline' unit test scenarios.
///
+ [Export(typeof(IDataSourceConnectionFactory))]
public class DataSourceConnectionFactory : IDataSourceConnectionFactory
{
///
diff --git a/src/Microsoft.Kusto.ServiceLayer/Connection/DatabaseFullAccessException.cs b/src/Microsoft.Kusto.ServiceLayer/Connection/DatabaseFullAccessException.cs
deleted file mode 100644
index f1a2d11c..00000000
--- a/src/Microsoft.Kusto.ServiceLayer/Connection/DatabaseFullAccessException.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-//
-// 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.Kusto.ServiceLayer.Connection
-{
- public class DatabaseFullAccessException: Exception
- {
- public DatabaseFullAccessException()
- : base()
- {
- }
-
- public DatabaseFullAccessException(string message, Exception exception)
- : base(message, exception)
- {
- }
-
-
- public DatabaseFullAccessException(string message)
- : base(message)
- {
- }
- }
-}
diff --git a/src/Microsoft.Kusto.ServiceLayer/Connection/DatabaseLocksManager.cs b/src/Microsoft.Kusto.ServiceLayer/Connection/DatabaseLocksManager.cs
index c1aeb0df..4fac28c5 100644
--- a/src/Microsoft.Kusto.ServiceLayer/Connection/DatabaseLocksManager.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/Connection/DatabaseLocksManager.cs
@@ -3,7 +3,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
-using Microsoft.Kusto.ServiceLayer.LanguageServices;
using System;
using System.Collections.Generic;
using System.Threading;
@@ -12,100 +11,23 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
{
public class DatabaseLocksManager: IDisposable
{
- internal DatabaseLocksManager(int waitToGetFullAccess)
- {
- this.waitToGetFullAccess = waitToGetFullAccess;
- }
-
- private static DatabaseLocksManager instance = new DatabaseLocksManager(DefaultWaitToGetFullAccess);
+ private static readonly DatabaseLocksManager _instance = new DatabaseLocksManager();
public static DatabaseLocksManager Instance
{
get
{
- return instance;
+ return _instance;
}
}
public ConnectionService ConnectionService { get; set; }
- private Dictionary databaseAccessEvents = new Dictionary();
- private object databaseAccessLock = new object();
- public const int DefaultWaitToGetFullAccess = 10000;
- public int waitToGetFullAccess = DefaultWaitToGetFullAccess;
-
- private ManualResetEvent GetResetEvent(string serverName, string databaseName)
- {
- string key = GenerateKey(serverName, databaseName);
- ManualResetEvent resetEvent = null;
- lock (databaseAccessLock)
- {
- if (!databaseAccessEvents.TryGetValue(key, out resetEvent))
- {
- resetEvent = new ManualResetEvent(true);
- databaseAccessEvents.Add(key, resetEvent);
- }
- }
-
- return resetEvent;
- }
-
- public bool GainFullAccessToDatabase(string serverName, string databaseName)
- {
- /*
- * TODO: add the lock so not two process can get full access at the same time
- ManualResetEvent resetEvent = GetResetEvent(serverName, databaseName);
- if (resetEvent.WaitOne(this.waitToGetFullAccess))
- {
- resetEvent.Reset();
-
- foreach (IConnectedBindingQueue item in ConnectionService.ConnectedQueues)
- {
- item.CloseConnections(serverName, databaseName);
- }
- return true;
- }
- else
- {
- throw new DatabaseFullAccessException($"Waited more than {waitToGetFullAccess} milli seconds for others to release the lock");
- }
- */
- foreach (IConnectedBindingQueue item in ConnectionService.ConnectedQueues)
- {
- item.CloseConnections(serverName, databaseName, DefaultWaitToGetFullAccess);
- }
- return true;
-
- }
-
- public bool ReleaseAccess(string serverName, string databaseName)
- {
- /*
- ManualResetEvent resetEvent = GetResetEvent(serverName, databaseName);
-
- foreach (IConnectedBindingQueue item in ConnectionService.ConnectedQueues)
- {
- item.OpenConnections(serverName, databaseName);
- }
-
- resetEvent.Set();
- */
- foreach (IConnectedBindingQueue item in ConnectionService.ConnectedQueues)
- {
- item.OpenConnections(serverName, databaseName, DefaultWaitToGetFullAccess);
- }
- return true;
-
- }
-
- private string GenerateKey(string serverName, string databaseName)
- {
- return $"{serverName.ToLowerInvariant()}-{databaseName.ToLowerInvariant()}";
- }
+ private readonly Dictionary _databaseAccessEvents = new Dictionary();
public void Dispose()
{
- foreach (var resetEvent in databaseAccessEvents)
+ foreach (var resetEvent in _databaseAccessEvents)
{
resetEvent.Value.Dispose();
}
diff --git a/src/Microsoft.Kusto.ServiceLayer/Connection/FeatureWithFullDbAccess.cs b/src/Microsoft.Kusto.ServiceLayer/Connection/FeatureWithFullDbAccess.cs
deleted file mode 100644
index 9be034c7..00000000
--- a/src/Microsoft.Kusto.ServiceLayer/Connection/FeatureWithFullDbAccess.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-//
-
-namespace Microsoft.Kusto.ServiceLayer.Connection
-{
- ///
- /// Any operation that needs full access to databas should implement this interface.
- /// Make sure to call GainAccessToDatabase before the operation and ReleaseAccessToDatabase after
- ///
- public interface IFeatureWithFullDbAccess
- {
- ///
- /// Database Lock Manager
- ///
- DatabaseLocksManager LockedDatabaseManager { get; set; }
-
- ///
- /// Makes sure the feature has fill access to the database
- ///
- bool GainAccessToDatabase();
-
- ///
- /// Release the access to db
- ///
- bool ReleaseAccessToDatabase();
-
- ///
- /// Server name
- ///
- string ServerName { get; }
-
- ///
- /// Database name
- ///
- string DatabaseName { get; }
- }
-
-}
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/DataReaderWrapper.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/DataReaderWrapper.cs
index 89ee3616..61caa060 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/DataReaderWrapper.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/DataReaderWrapper.cs
@@ -2,13 +2,11 @@
// Copyright (c) Microsoft. All Rights Reserved.
//
using System;
-using System.Text.RegularExpressions;
-using System.Linq;
using System.Data;
namespace Microsoft.Kusto.ServiceLayer.DataSource
{
- class DataReaderWrapper:IDataReader
+ public class DataReaderWrapper : IDataReader
{
private readonly IDataReader _inner ;
public DataReaderWrapper(IDataReader inner)
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceFactory.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceFactory.cs
index 3e78d500..59678a51 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceFactory.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceFactory.cs
@@ -1,22 +1,15 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using Microsoft.Kusto.ServiceLayer.Utility;
-using Microsoft.Kusto.ServiceLayer.Admin.Contracts;
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
-using Microsoft.Kusto.ServiceLayer.Metadata.Contracts;
using Microsoft.Kusto.ServiceLayer.DataSource.DataSourceIntellisense;
-using Microsoft.Kusto.ServiceLayer.DataSource.Metadata;
using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts;
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
using Microsoft.Kusto.ServiceLayer.LanguageServices.Completion;
namespace Microsoft.Kusto.ServiceLayer.DataSource
{
- ///
- /// Data source factory.
- ///
- public static class DataSourceFactory
+ public class DataSourceFactory
{
public static IDataSource Create(DataSourceType dataSourceType, string connectionString, string azureAccountToken)
{
@@ -26,57 +19,16 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
switch (dataSourceType)
{
case DataSourceType.Kusto:
- {
- return new KustoDataSource(connectionString, azureAccountToken);
- }
+ {
+ return new KustoDataSource(connectionString, azureAccountToken);
+ }
default:
- throw new ArgumentException($"Unsupported data source type \"{dataSourceType}\"", nameof(dataSourceType));
+ throw new ArgumentException($"Unsupported data source type \"{dataSourceType}\"",
+ nameof(dataSourceType));
}
}
- public static DataSourceObjectMetadata CreateClusterMetadata(string clusterName)
- {
- ValidationUtils.IsArgumentNotNullOrWhiteSpace(clusterName, nameof(clusterName));
-
- return new DataSourceObjectMetadata{
- MetadataType = DataSourceMetadataType.Cluster,
- MetadataTypeName = DataSourceMetadataType.Cluster.ToString(),
- Name = clusterName,
- PrettyName = clusterName,
- Urn = $"{clusterName}"
- };
- }
-
- public static DataSourceObjectMetadata CreateDatabaseMetadata(DataSourceObjectMetadata clusterMetadata, string databaseName)
- {
- ValidationUtils.IsTrue(clusterMetadata.MetadataType == DataSourceMetadataType.Cluster, nameof(clusterMetadata));
- ValidationUtils.IsArgumentNotNullOrWhiteSpace(databaseName, nameof(databaseName));
-
- return new DatabaseMetadata{
- ClusterName = clusterMetadata.Name,
- MetadataType = DataSourceMetadataType.Database,
- MetadataTypeName = DataSourceMetadataType.Database.ToString(),
- Name = databaseName,
- PrettyName = databaseName,
- Urn = $"{clusterMetadata.Urn}.{databaseName}"
- };
- }
-
- public static FolderMetadata CreateFolderMetadata(DataSourceObjectMetadata parentMetadata, string path, string name)
- {
- ValidationUtils.IsNotNull(parentMetadata, nameof(parentMetadata));
-
- return new FolderMetadata{
- MetadataType = DataSourceMetadataType.Folder,
- MetadataTypeName = DataSourceMetadataType.Folder.ToString(),
- Name = name,
- PrettyName = name,
- ParentMetadata = parentMetadata,
- Urn = $"{path}.{name}"
- };
- }
-
// Gets default keywords for intellisense when there is no connection.
public static CompletionItem[] GetDefaultAutoComplete(DataSourceType dataSourceType, ScriptDocumentInfo scriptDocumentInfo, Position textDocumentPosition){
switch (dataSourceType)
@@ -105,41 +57,6 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
}
}
- // Converts database details shown on cluster manage dashboard to DatabaseInfo type. Add DataSourceType as param if required to show different properties
- public static List ConvertToDatabaseInfo(IEnumerable clusterDBDetails)
- {
- var databaseDetails = new List();
-
- if(typeof(DatabaseMetadata) == clusterDBDetails.FirstOrDefault().GetType()){
- foreach(var dbDetail in clusterDBDetails)
- {
- DatabaseInfo databaseInfo = new DatabaseInfo();
- Int64.TryParse(dbDetail.SizeInMB.ToString(), out long sum_OriginalSize);
- databaseInfo.Options["name"] = dbDetail.Name;
- databaseInfo.Options["sizeInMB"] = (sum_OriginalSize /(1024 * 1024)).ToString();
- databaseDetails.Add(databaseInfo);
- }
- }
-
- return databaseDetails;
- }
-
- // Converts tables details shown on database manage dashboard to ObjectMetadata type. Add DataSourceType as param if required to show different properties
- public static List ConvertToObjectMetadata(IEnumerable dbChildDetails)
- {
- var databaseChildDetails = new List();
-
- foreach(var childDetail in dbChildDetails)
- {
- ObjectMetadata dbChildInfo = new ObjectMetadata();
- dbChildInfo.Name = childDetail.PrettyName;
- dbChildInfo.MetadataTypeName = childDetail.MetadataTypeName;
- dbChildInfo.MetadataType = MetadataType.Table; // Add mapping here.
- databaseChildDetails.Add(dbChildInfo);
- }
- return databaseChildDetails;
- }
-
public static ReliableConnectionHelper.ServerInfo ConvertToServerinfoFormat(DataSourceType dataSourceType, DiagnosticsInfo clusterDiagnostics)
{
switch (dataSourceType)
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceIntellisense/KustoIntellisenseHelper.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceIntellisense/KustoIntellisenseHelper.cs
index fa423e3c..75677e1f 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceIntellisense/KustoIntellisenseHelper.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceIntellisense/KustoIntellisenseHelper.cs
@@ -22,9 +22,8 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource.DataSourceIntellisense
///
/// Kusto specific class for intellisense helper functions.
///
- public static class KustoIntellisenseHelper
+ public class KustoIntellisenseHelper
{
-
public class ShowDatabasesResult
{
public string DatabaseName;
@@ -162,7 +161,7 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource.DataSourceIntellisense
///
/// Loads the schema for the specified databasea into a a .
///
- public static async Task LoadDatabaseAsync(IDataSource dataSource, string databaseName, bool throwOnError = false)
+ private static async Task LoadDatabaseAsync(IDataSource dataSource, string databaseName, bool throwOnError = false)
{
var members = new List();
CancellationTokenSource source = new CancellationTokenSource();
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/KustoDataSource.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/KustoDataSource.cs
index f54ffb86..7cba553c 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/KustoDataSource.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/KustoDataSource.cs
@@ -88,9 +88,12 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
ClusterName = GetClusterName(connectionString);
DatabaseName = GetDatabaseName(connectionString);
UserToken = azureAccountToken;
- SchemaState = Task.Run(() => KustoIntellisenseHelper.AddOrUpdateDatabaseAsync(this, GlobalState.Default, DatabaseName, ClusterName, throwOnError: false)).Result;
+ SchemaState = Task.Run(() =>
+ KustoIntellisenseHelper.AddOrUpdateDatabaseAsync(this, GlobalState.Default, DatabaseName, ClusterName,
+ throwOnError: false)).Result;
// Check if a connection can be made
- ValidationUtils.IsTrue(Exists().Result, $"Unable to connect. ClusterName = {ClusterName}, DatabaseName = {DatabaseName}");
+ ValidationUtils.IsTrue(Exists().Result,
+ $"Unable to connect. ClusterName = {ClusterName}, DatabaseName = {DatabaseName}");
}
///
@@ -736,7 +739,7 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
if (tableInfos.Any(x => !string.IsNullOrWhiteSpace(x.Folder)))
{
// create Table folder to hold functions tables
- var tableFolder = DataSourceFactory.CreateFolderMetadata(databaseMetadata, rootTableFolderKey.ToString(), "Tables");
+ var tableFolder = MetadataFactory.CreateFolderMetadata(databaseMetadata, rootTableFolderKey.ToString(), "Tables");
_folderMetadata.AddRange(rootTableFolderKey.ToString(), new List {tableFolder});
rootTableFolderKey.Append($".{tableFolder.Name}");
@@ -782,7 +785,7 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
continue;
}
- var folder = DataSourceFactory.CreateFolderMetadata(objectMetadata, rootTableFolderKey, columnGroup.Key);
+ var folder = MetadataFactory.CreateFolderMetadata(objectMetadata, rootTableFolderKey, columnGroup.Key);
tableFolders.Add(folder);
}
@@ -800,7 +803,7 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
// create Functions folder to hold functions folders
var rootFunctionFolderKey = $"{databaseMetadata.Urn}";
- var rootFunctionFolder = DataSourceFactory.CreateFolderMetadata(databaseMetadata, rootFunctionFolderKey, "Functions");
+ var rootFunctionFolder = MetadataFactory.CreateFolderMetadata(databaseMetadata, rootFunctionFolderKey, "Functions");
_folderMetadata.AddRange(rootFunctionFolderKey, new List {rootFunctionFolder});
// create each folder to hold functions
@@ -834,13 +837,13 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
var topFolder = subFolders.First();
var folderKey = functionFolder.Urn;
- var folder = DataSourceFactory.CreateFolderMetadata(databaseMetadata, folderKey, topFolder);
+ var folder = MetadataFactory.CreateFolderMetadata(databaseMetadata, folderKey, topFolder);
functionFolders.SafeAdd(folderKey, folder);
for (int i = 1; i < subFolders.Length; i++)
{
folderKey = $"{folderKey}.{subFolders[i - 1]}";
- var subFolder = DataSourceFactory.CreateFolderMetadata(databaseMetadata, folderKey, subFolders[i]);
+ var subFolder = MetadataFactory.CreateFolderMetadata(databaseMetadata, folderKey, subFolders[i]);
functionFolders.SafeAdd(folderKey, subFolder);
}
}
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/KustoResultsReader.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/KustoResultsReader.cs
index c5bea819..9e9207ca 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/KustoResultsReader.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/KustoResultsReader.cs
@@ -4,19 +4,18 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
{
internal class KustoResultsReader : DataReaderWrapper
{
- public KustoResultsReader(IDataReader reader)
- : base(reader)
+ public KustoResultsReader(IDataReader reader) : base(reader)
{
}
-
+
///
- /// Kusto returns 3 results tables - QueryResults, QueryProperties, QueryStatus. When returning query results
- /// we want the caller to only read the first table. We override the NextResult function here to only return one table
- /// from the IDataReader.
- ///
- /*public override bool NextResult()
- {
- return false;
+ /// Kusto returns 3 results tables - QueryResults, QueryProperties, QueryStatus. When returning query results
+ /// we want the caller to only read the first table. We override the NextResult function here to only return one table
+ /// from the IDataReader.
+ ///
+ /*public override bool NextResult()
+ {
+ return false;
}*/
}
}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/Metadata/MetadataFactory.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Metadata/MetadataFactory.cs
new file mode 100644
index 00000000..ba846c36
--- /dev/null
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/Metadata/MetadataFactory.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Kusto.ServiceLayer.Admin.Contracts;
+using Microsoft.Kusto.ServiceLayer.Metadata.Contracts;
+using Microsoft.Kusto.ServiceLayer.Utility;
+
+namespace Microsoft.Kusto.ServiceLayer.DataSource.Metadata
+{
+ public class MetadataFactory
+ {
+ public static DataSourceObjectMetadata CreateClusterMetadata(string clusterName)
+ {
+ ValidationUtils.IsArgumentNotNullOrWhiteSpace(clusterName, nameof(clusterName));
+
+ return new DataSourceObjectMetadata
+ {
+ MetadataType = DataSourceMetadataType.Cluster,
+ MetadataTypeName = DataSourceMetadataType.Cluster.ToString(),
+ Name = clusterName,
+ PrettyName = clusterName,
+ Urn = $"{clusterName}"
+ };
+ }
+
+ public static DataSourceObjectMetadata CreateDatabaseMetadata(DataSourceObjectMetadata clusterMetadata,
+ string databaseName)
+ {
+ ValidationUtils.IsTrue(clusterMetadata.MetadataType == DataSourceMetadataType.Cluster,
+ nameof(clusterMetadata));
+ ValidationUtils.IsArgumentNotNullOrWhiteSpace(databaseName, nameof(databaseName));
+
+ return new DatabaseMetadata
+ {
+ ClusterName = clusterMetadata.Name,
+ MetadataType = DataSourceMetadataType.Database,
+ MetadataTypeName = DataSourceMetadataType.Database.ToString(),
+ Name = databaseName,
+ PrettyName = databaseName,
+ Urn = $"{clusterMetadata.Urn}.{databaseName}"
+ };
+ }
+
+ public static FolderMetadata CreateFolderMetadata(DataSourceObjectMetadata parentMetadata, string path, string name)
+ {
+ ValidationUtils.IsNotNull(parentMetadata, nameof(parentMetadata));
+
+ return new FolderMetadata
+ {
+ MetadataType = DataSourceMetadataType.Folder,
+ MetadataTypeName = DataSourceMetadataType.Folder.ToString(),
+ Name = name,
+ PrettyName = name,
+ ParentMetadata = parentMetadata,
+ Urn = $"{path}.{name}"
+ };
+ }
+
+ ///
+ /// Converts database details shown on cluster manage dashboard to DatabaseInfo type. Add DataSourceType as param if required to show different properties
+ ///
+ ///
+ ///
+ public static List ConvertToDatabaseInfo(IEnumerable clusterDBDetails)
+ {
+ var databaseDetails = new List();
+
+ if (typeof(DatabaseMetadata) == clusterDBDetails.FirstOrDefault().GetType())
+ {
+ foreach (var dbDetail in clusterDBDetails)
+ {
+ DatabaseInfo databaseInfo = new DatabaseInfo();
+ Int64.TryParse(dbDetail.SizeInMB.ToString(), out long sum_OriginalSize);
+ databaseInfo.Options["name"] = dbDetail.Name;
+ databaseInfo.Options["sizeInMB"] = (sum_OriginalSize / (1024 * 1024)).ToString();
+ databaseDetails.Add(databaseInfo);
+ }
+ }
+
+ return databaseDetails;
+ }
+
+ ///
+ /// Converts tables details shown on database manage dashboard to ObjectMetadata type. Add DataSourceType as param if required to show different properties
+ ///
+ ///
+ ///
+ public static List ConvertToObjectMetadata(IEnumerable dbChildDetails)
+ {
+ var databaseChildDetails = new List();
+
+ foreach (var childDetail in dbChildDetails)
+ {
+ ObjectMetadata dbChildInfo = new ObjectMetadata();
+ dbChildInfo.Name = childDetail.PrettyName;
+ dbChildInfo.MetadataTypeName = childDetail.MetadataTypeName;
+ dbChildInfo.MetadataType = MetadataType.Table; // Add mapping here.
+ databaseChildDetails.Add(dbChildInfo);
+ }
+
+ return databaseChildDetails;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/ReliableDataSourceConnection.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/ReliableDataSourceConnection.cs
index dd1e0bb5..a45ad9d4 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/ReliableDataSourceConnection.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/ReliableDataSourceConnection.cs
@@ -21,16 +21,8 @@
// =======================================================================================
using System;
-using System.Collections.Generic;
-using System.Data;
-using System.Collections;
-using System.Data.Common;
using System.Data.SqlClient;
-using System.Diagnostics;
-using System.Globalization;
-using System.Text;
using System.Threading;
-using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.SqlTools.Utility;
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
@@ -42,15 +34,15 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
/// Provides a reliable way of opening connections to and executing commands
/// taking into account potential network unreliability and a requirement for connection retry.
///
- public sealed partial class ReliableDataSourceConnection : IDisposable
+ public sealed class ReliableDataSourceConnection : IDisposable
{
private IDataSource _dataSource;
private readonly RetryPolicy _connectionRetryPolicy;
private RetryPolicy _commandRetryPolicy;
- private Guid _azureSessionId = Guid.NewGuid();
+ private readonly Guid _azureSessionId = Guid.NewGuid();
- private string _connectionString;
- private string _azureAccountToken;
+ private readonly string _connectionString;
+ private readonly string _azureAccountToken;
///
/// Initializes a new instance of the ReliableKustoClient class with a given connection string
diff --git a/src/Microsoft.Kusto.ServiceLayer/HostLoader.cs b/src/Microsoft.Kusto.ServiceLayer/HostLoader.cs
index c0c0e187..d6e577b3 100644
--- a/src/Microsoft.Kusto.ServiceLayer/HostLoader.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/HostLoader.cs
@@ -11,7 +11,8 @@ using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.Kusto.ServiceLayer.Admin;
using Microsoft.Kusto.ServiceLayer.Metadata;
using Microsoft.Kusto.ServiceLayer.Connection;
-using Microsoft.Kusto.ServiceLayer.Hosting;
+using Microsoft.Kusto.ServiceLayer.DataSource;
+using Microsoft.Kusto.ServiceLayer.DataSource.Metadata;
using Microsoft.Kusto.ServiceLayer.LanguageServices;
using Microsoft.Kusto.ServiceLayer.QueryExecution;
using Microsoft.Kusto.ServiceLayer.Scripting;
@@ -66,6 +67,10 @@ namespace Microsoft.Kusto.ServiceLayer
ExtensionServiceProvider serviceProvider = ExtensionServiceProvider.CreateDefaultServiceProvider(inclusionList);
serviceProvider.RegisterSingleService(sqlToolsContext);
serviceProvider.RegisterSingleService(serviceHost);
+
+ var scripter = serviceProvider.GetService();
+ var dataSourceConnectionFactory = serviceProvider.GetService();
+ var connectedBindingQueue = serviceProvider.GetService();
// Initialize and register singleton services so they're accessible for any MEF service. In the future, these
// could be updated to be IComposableServices, which would avoid the requirement to define a singleton instance
@@ -73,10 +78,10 @@ namespace Microsoft.Kusto.ServiceLayer
WorkspaceService.Instance.InitializeService(serviceHost);
serviceProvider.RegisterSingleService(WorkspaceService.Instance);
- LanguageService.Instance.InitializeService(serviceHost, sqlToolsContext);
+ LanguageService.Instance.InitializeService(serviceHost, connectedBindingQueue);
serviceProvider.RegisterSingleService(LanguageService.Instance);
- ConnectionService.Instance.InitializeService(serviceHost);
+ ConnectionService.Instance.InitializeService(serviceHost, dataSourceConnectionFactory, connectedBindingQueue);
serviceProvider.RegisterSingleService(ConnectionService.Instance);
CredentialService.Instance.InitializeService(serviceHost);
@@ -85,7 +90,7 @@ namespace Microsoft.Kusto.ServiceLayer
QueryExecutionService.Instance.InitializeService(serviceHost);
serviceProvider.RegisterSingleService(QueryExecutionService.Instance);
- ScriptingService.Instance.InitializeService(serviceHost);
+ ScriptingService.Instance.InitializeService(serviceHost, scripter);
serviceProvider.RegisterSingleService(ScriptingService.Instance);
AdminService.Instance.InitializeService(serviceHost);
@@ -93,7 +98,7 @@ namespace Microsoft.Kusto.ServiceLayer
MetadataService.Instance.InitializeService(serviceHost);
serviceProvider.RegisterSingleService(MetadataService.Instance);
-
+
InitializeHostedServices(serviceProvider, serviceHost);
serviceHost.ServiceProvider = serviceProvider;
diff --git a/src/Microsoft.Kusto.ServiceLayer/LanguageServices/AutoCompleteHelper.cs b/src/Microsoft.Kusto.ServiceLayer/LanguageServices/AutoCompleteHelper.cs
index 509e3722..79c5ac91 100644
--- a/src/Microsoft.Kusto.ServiceLayer/LanguageServices/AutoCompleteHelper.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/LanguageServices/AutoCompleteHelper.cs
@@ -2,6 +2,7 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
+
using Range = Microsoft.Kusto.ServiceLayer.Workspace.Contracts.Range;
using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts;
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
@@ -11,7 +12,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
///
/// All the conversion of intellisense info to vscode format is done in this class.
///
- public static class AutoCompleteHelper
+ public class AutoCompleteHelper
{
///
/// Create a completion item from the default item text since VS Code expects CompletionItems
diff --git a/src/Microsoft.Kusto.ServiceLayer/LanguageServices/ConnectedBindingQueue.cs b/src/Microsoft.Kusto.ServiceLayer/LanguageServices/ConnectedBindingQueue.cs
index 49391ea8..dd7405b9 100644
--- a/src/Microsoft.Kusto.ServiceLayer/LanguageServices/ConnectedBindingQueue.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/LanguageServices/ConnectedBindingQueue.cs
@@ -4,8 +4,7 @@
//
using System;
-using System.Data.SqlClient;
-using Microsoft.SqlServer.Management.Common;
+using System.Composition;
using Microsoft.SqlServer.Management.SmoMetadataProvider;
using Microsoft.SqlServer.Management.SqlParser.Binder;
using Microsoft.SqlServer.Management.SqlParser.MetadataProvider;
@@ -14,81 +13,36 @@ using Microsoft.Kusto.ServiceLayer.Connection.Contracts;
using Microsoft.Kusto.ServiceLayer.SqlContext;
using Microsoft.Kusto.ServiceLayer.Workspace;
using Microsoft.Kusto.ServiceLayer.DataSource;
-using System.Threading;
namespace Microsoft.Kusto.ServiceLayer.LanguageServices
{
- public interface IConnectedBindingQueue
- {
- void CloseConnections(string serverName, string databaseName, int millisecondsTimeout);
- void OpenConnections(string serverName, string databaseName, int millisecondsTimeout);
- string AddConnectionContext(ConnectionInfo connInfo, string featureName = null, bool overwrite = false);
- void Dispose();
- QueueItem QueueBindingOperation(
- string key,
- Func bindOperation,
- Func timeoutOperation = null,
- Func errorHandler = null,
- int? bindingTimeout = null,
- int? waitForLockTimeout = null);
- }
-
- public class SqlConnectionOpener
- {
- ///
- /// Virtual method used to support mocking and testing
- ///
- public virtual ServerConnection OpenServerConnection(ConnectionInfo connInfo, string featureName)
- {
- return ConnectionService.OpenServerConnection(connInfo, featureName);
- }
- }
-
///
/// ConnectedBindingQueue class for processing online binding requests
///
+ [Export(typeof(IConnectedBindingQueue))]
public class ConnectedBindingQueue : BindingQueue, IConnectedBindingQueue
{
internal const int DefaultBindingTimeout = 500;
-
- internal const int DefaultMinimumConnectionTimeout = 30;
-
- ///
- /// flag determing if the connection queue requires online metadata objects
- /// it's much cheaper to not construct these objects if not needed
- ///
- private bool needsMetadata;
- private SqlConnectionOpener connectionOpener;
+ private readonly ISqlConnectionOpener _connectionOpener;
///
/// Gets the current settings
///
- internal SqlToolsSettings CurrentSettings
+ private SqlToolsSettings CurrentSettings
{
get { return WorkspaceService.Instance.CurrentSettings; }
}
- public ConnectedBindingQueue()
- : this(true)
- {
- }
-
- public ConnectedBindingQueue(bool needsMetadata)
+ [ImportingConstructor]
+ public ConnectedBindingQueue(ISqlConnectionOpener sqlConnectionOpener)
{
- this.needsMetadata = needsMetadata;
- this.connectionOpener = new SqlConnectionOpener();
- }
-
- // For testing purposes only
- internal void SetConnectionOpener(SqlConnectionOpener opener)
- {
- this.connectionOpener = opener;
+ _connectionOpener = sqlConnectionOpener;
}
///
/// Generate a unique key based on the ConnectionInfo object
///
- ///
+ ///
internal static string GetConnectionContextKey(ConnectionDetails details)
{
string key = string.Format("{0}_{1}_{2}_{3}",
@@ -114,7 +68,8 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
///
/// Generate a unique key based on the ConnectionInfo object
///
- ///
+ ///
+ ///
private string GetConnectionContextKey(string serverName, string databaseName)
{
return string.Format("{0}_{1}",
@@ -156,7 +111,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
}
}
- public void RemoveBindigContext(ConnectionInfo connInfo)
+ public void RemoveBindingContext(ConnectionInfo connInfo)
{
string connectionKey = GetConnectionContextKey(connInfo.ConnectionDetails);
if (BindingContextExists(connectionKey))
@@ -168,9 +123,11 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
///
/// Use a ConnectionInfo item to create a connected binding context
///
- /// Connection info used to create binding context
- /// Overwrite existing context
- public virtual string AddConnectionContext(ConnectionInfo connInfo, string featureName = null, bool overwrite = false)
+ /// Connection info used to create binding context
+ ///
+ ///
+ /// Overwrite existing context
+ public string AddConnectionContext(ConnectionInfo connInfo, bool needMetadata, string featureName = null, bool overwrite = false)
{
if (connInfo == null)
{
@@ -191,7 +148,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
return connectionKey;
}
}
- IBindingContext bindingContext = this.GetOrCreateBindingContext(connectionKey);
+ IBindingContext bindingContext = GetOrCreateBindingContext(connectionKey);
if (bindingContext.BindingLock.WaitOne())
{
@@ -200,12 +157,12 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
bindingContext.BindingLock.Reset();
// populate the binding context to work with the SMO metadata provider
- bindingContext.ServerConnection = connectionOpener.OpenServerConnection(connInfo, featureName);
+ bindingContext.ServerConnection = _connectionOpener.OpenServerConnection(connInfo, featureName);
string connectionString = ConnectionService.BuildConnectionString(connInfo.ConnectionDetails);
bindingContext.DataSource = DataSourceFactory.Create(DataSourceType.Kusto, connectionString, connInfo.ConnectionDetails.AzureAccountToken);
- if (this.needsMetadata)
+ if (needMetadata)
{
bindingContext.SmoMetadataProvider = SmoMetadataProvider.CreateConnectedProvider(bindingContext.ServerConnection);
bindingContext.MetadataDisplayInfoProvider = new MetadataDisplayInfoProvider();
diff --git a/src/Microsoft.Kusto.ServiceLayer/LanguageServices/IConnectedBindingQueue.cs b/src/Microsoft.Kusto.ServiceLayer/LanguageServices/IConnectedBindingQueue.cs
new file mode 100644
index 00000000..f777fdb3
--- /dev/null
+++ b/src/Microsoft.Kusto.ServiceLayer/LanguageServices/IConnectedBindingQueue.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Threading;
+using Microsoft.Kusto.ServiceLayer.Connection;
+
+namespace Microsoft.Kusto.ServiceLayer.LanguageServices
+{
+ public interface IConnectedBindingQueue
+ {
+ event BindingQueue.UnhandledExceptionDelegate OnUnhandledException;
+
+ void CloseConnections(string serverName, string databaseName, int millisecondsTimeout);
+ void OpenConnections(string serverName, string databaseName, int millisecondsTimeout);
+ string AddConnectionContext(ConnectionInfo connInfo, bool needMetadata, string featureName = null, bool overwrite = false);
+ void Dispose();
+ bool IsBindingContextConnected(string key);
+ void RemoveBindingContext(ConnectionInfo connInfo);
+
+ QueueItem QueueBindingOperation(
+ string key,
+ Func bindOperation,
+ Func timeoutOperation = null,
+ Func errorHandler = null,
+ int? bindingTimeout = null,
+ int? waitForLockTimeout = null);
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/LanguageServices/ISqlConnectionOpener.cs b/src/Microsoft.Kusto.ServiceLayer/LanguageServices/ISqlConnectionOpener.cs
new file mode 100644
index 00000000..a2eba050
--- /dev/null
+++ b/src/Microsoft.Kusto.ServiceLayer/LanguageServices/ISqlConnectionOpener.cs
@@ -0,0 +1,13 @@
+using Microsoft.Kusto.ServiceLayer.Connection;
+using Microsoft.SqlServer.Management.Common;
+
+namespace Microsoft.Kusto.ServiceLayer.LanguageServices
+{
+ public interface ISqlConnectionOpener
+ {
+ ///
+ /// Virtual method used to support mocking and testing
+ ///
+ ServerConnection OpenServerConnection(ConnectionInfo connInfo, string featureName);
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/LanguageServices/LanguageService.cs b/src/Microsoft.Kusto.ServiceLayer/LanguageServices/LanguageService.cs
index a2ed3e6e..7a347456 100644
--- a/src/Microsoft.Kusto.ServiceLayer/LanguageServices/LanguageService.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/LanguageServices/LanguageService.cs
@@ -18,10 +18,8 @@ using Microsoft.Kusto.ServiceLayer.DataSource;
using Microsoft.Kusto.ServiceLayer.DataSource.DataSourceIntellisense;
using Microsoft.Kusto.ServiceLayer.Connection;
using Microsoft.Kusto.ServiceLayer.Connection.Contracts;
-using Microsoft.Kusto.ServiceLayer.Hosting;
using Microsoft.Kusto.ServiceLayer.LanguageServices.Completion;
using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts;
-using SqlToolsContext = Microsoft.SqlTools.ServiceLayer.SqlContext.SqlToolsContext;
using Microsoft.Kusto.ServiceLayer.Utility;
using Microsoft.Kusto.ServiceLayer.Workspace;
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
@@ -82,7 +80,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
private ScriptParseInfo currentCompletionParseInfo;
- private ConnectedBindingQueue bindingQueue = new ConnectedBindingQueue();
+ private IConnectedBindingQueue _bindingQueue;
private ParseOptions defaultParseOptions = new ParseOptions(
batchSeparator: LanguageService.DefaultBatchSeperator,
@@ -125,22 +123,6 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
#region Properties
- ///
- /// Gets or sets the binding queue instance
- /// Internal for testing purposes only
- ///
- internal ConnectedBindingQueue BindingQueue
- {
- get
- {
- return this.bindingQueue;
- }
- set
- {
- this.bindingQueue = value;
- }
- }
-
///
/// Internal for testing purposes only
///
@@ -151,7 +133,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
if (connectionService == null)
{
connectionService = ConnectionService.Instance;
- connectionService.RegisterConnectedQueue("LanguageService", bindingQueue);
+ connectionService.RegisterConnectedQueue("LanguageService", _bindingQueue);
}
return connectionService;
}
@@ -216,12 +198,6 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
get { return WorkspaceServiceInstance.Workspace; }
}
- ///
- /// Gets or sets the current SQL Tools context
- ///
- ///
- internal SqlToolsContext Context { get; set; }
-
#endregion
#region Public Methods
@@ -231,23 +207,20 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
///
///
///
- public void InitializeService(ServiceHost serviceHost, SqlToolsContext context)
+ ///
+ ///
+ public void InitializeService(ServiceHost serviceHost, IConnectedBindingQueue connectedBindingQueue)
{
+ _bindingQueue = connectedBindingQueue;
// Register the requests that this service will handle
- // turn off until needed (10/28/2016)
- // serviceHost.SetRequestHandler(ReferencesRequest.Type, HandleReferencesRequest);
- // serviceHost.SetRequestHandler(DocumentHighlightRequest.Type, HandleDocumentHighlightRequest);
-
//serviceHost.SetRequestHandler(SignatureHelpRequest.Type, HandleSignatureHelpRequest); // Kusto api doesnt support this as of now. Implement it wherever applicable. Hover help is closest to signature help
serviceHost.SetRequestHandler(CompletionResolveRequest.Type, HandleCompletionResolveRequest);
serviceHost.SetRequestHandler(HoverRequest.Type, HandleHoverRequest);
serviceHost.SetRequestHandler(CompletionRequest.Type, HandleCompletionRequest);
serviceHost.SetRequestHandler(DefinitionRequest.Type, HandleDefinitionRequest); // Parses "Go to definition" functionality
serviceHost.SetRequestHandler(SyntaxParseRequest.Type, HandleSyntaxParseRequest); // Parses syntax errors
- //serviceHost.SetRequestHandler(CompletionExtLoadRequest.Type, HandleCompletionExtLoadRequest);
serviceHost.SetEventHandler(RebuildIntelliSenseNotification.Type, HandleRebuildIntelliSenseNotification);
- //serviceHost.SetEventHandler(LanguageFlavorChangeNotification.Type, HandleDidChangeLanguageFlavorNotification);
// Register a no-op shutdown task for validation of the shutdown logic
serviceHost.RegisterShutdownTask(async (shutdownParams, shutdownRequestContext) =>
@@ -274,10 +247,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
// Register a callback for when a connection is closed
ConnectionServiceInstance.RegisterOnDisconnectTask(RemoveAutoCompleteCacheUriReference);
-
- // Store the SqlToolsContext for future use
- Context = context;
-
+
}
#endregion
@@ -599,7 +569,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
{
try
{
- this.BindingQueue.AddConnectionContext(connInfo, featureName: "LanguageService", overwrite: true);
+ _bindingQueue.AddConnectionContext(connInfo, true, featureName: "LanguageService", overwrite: true);
RemoveScriptParseInfo(rebuildParams.OwnerUri);
UpdateLanguageServiceOnConnection(connInfo).Wait();
}
@@ -724,8 +694,8 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
{
try
{
- scriptInfo.ConnectionKey = this.BindingQueue.AddConnectionContext(connInfo, "languageService");
- scriptInfo.IsConnected = this.BindingQueue.IsBindingContextConnected(scriptInfo.ConnectionKey);
+ scriptInfo.ConnectionKey = _bindingQueue.AddConnectionContext(connInfo, true,"languageService");
+ scriptInfo.IsConnected = _bindingQueue.IsBindingContextConnected(scriptInfo.ConnectionKey);
}
catch (Exception ex)
{
@@ -822,13 +792,6 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
///
internal Hover GetHoverItem(TextDocumentPosition textDocumentPosition, ScriptFile scriptFile)
{
- int startLine = textDocumentPosition.Position.Line;
- int startColumn = TextUtilities.PositionOfPrevDelimeter(
- scriptFile.Contents,
- textDocumentPosition.Position.Line,
- textDocumentPosition.Position.Character);
- int endColumn = textDocumentPosition.Position.Character;
-
ScriptParseInfo scriptParseInfo = GetScriptParseInfo(scriptFile.ClientUri);
ConnectionInfo connInfo;
ConnectionServiceInstance.TryFindConnection(
@@ -841,7 +804,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
{
try
{
- QueueItem queueItem = this.BindingQueue.QueueBindingOperation(
+ QueueItem queueItem = _bindingQueue.QueueBindingOperation(
key: scriptParseInfo.ConnectionKey,
bindingTimeout: LanguageService.HoverTimeout,
bindOperation: (bindingContext, cancelToken) =>
@@ -1191,9 +1154,9 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
public void Dispose()
{
- if (bindingQueue != null)
+ if (_bindingQueue != null)
{
- bindingQueue.Dispose();
+ _bindingQueue.Dispose();
}
}
}
diff --git a/src/Microsoft.Kusto.ServiceLayer/LanguageServices/SqlConnectionOpener.cs b/src/Microsoft.Kusto.ServiceLayer/LanguageServices/SqlConnectionOpener.cs
new file mode 100644
index 00000000..eb010220
--- /dev/null
+++ b/src/Microsoft.Kusto.ServiceLayer/LanguageServices/SqlConnectionOpener.cs
@@ -0,0 +1,15 @@
+using System.Composition;
+using Microsoft.Kusto.ServiceLayer.Connection;
+using Microsoft.SqlServer.Management.Common;
+
+namespace Microsoft.Kusto.ServiceLayer.LanguageServices
+{
+ [Export(typeof(ISqlConnectionOpener))]
+ public class SqlConnectionOpener : ISqlConnectionOpener
+ {
+ public ServerConnection OpenServerConnection(ConnectionInfo connInfo, string featureName)
+ {
+ return ConnectionService.OpenServerConnection(connInfo, featureName);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/Metadata/MetadataService.cs b/src/Microsoft.Kusto.ServiceLayer/Metadata/MetadataService.cs
index 36015f55..61582a3b 100644
--- a/src/Microsoft.Kusto.ServiceLayer/Metadata/MetadataService.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/Metadata/MetadataService.cs
@@ -4,12 +4,10 @@
//
using System;
-using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.Kusto.ServiceLayer.Connection;
-using Microsoft.Kusto.ServiceLayer.Hosting;
using Microsoft.Kusto.ServiceLayer.Metadata.Contracts;
using Microsoft.Kusto.ServiceLayer.Utility;
using Microsoft.Kusto.ServiceLayer.DataSource;
@@ -22,7 +20,7 @@ namespace Microsoft.Kusto.ServiceLayer.Metadata
///
public sealed class MetadataService
{
- private static readonly Lazy LazyInstance = new Lazy(() => new MetadataService());
+ private static readonly Lazy LazyInstance = new Lazy();
public static MetadataService Instance => LazyInstance.Value;
@@ -52,8 +50,8 @@ namespace Microsoft.Kusto.ServiceLayer.Metadata
/// Initializes the Metadata Service instance
///
///
- ///
- public void InitializeService(ServiceHost serviceHost)
+ ///
+ public void InitializeService(IProtocolEndpoint serviceHost)
{
serviceHost.SetRequestHandler(MetadataListRequest.Type, HandleMetadataListRequest);
}
@@ -79,11 +77,11 @@ namespace Microsoft.Kusto.ServiceLayer.Metadata
connInfo.TryGetConnection("Default", out connection);
IDataSource dataSource = connection.GetUnderlyingConnection();
- DataSourceObjectMetadata objectMetadata = DataSourceFactory.CreateClusterMetadata(connInfo.ConnectionDetails.ServerName);
- DataSourceObjectMetadata databaseMetadata = DataSourceFactory.CreateDatabaseMetadata(objectMetadata, connInfo.ConnectionDetails.DatabaseName);
+ DataSourceObjectMetadata objectMetadata = MetadataFactory.CreateClusterMetadata(connInfo.ConnectionDetails.ServerName);
+ DataSourceObjectMetadata databaseMetadata = MetadataFactory.CreateDatabaseMetadata(objectMetadata, connInfo.ConnectionDetails.DatabaseName);
IEnumerable databaseChildMetadataInfo = dataSource.GetChildObjects(databaseMetadata, true);
- metadata = DataSourceFactory.ConvertToObjectMetadata(databaseChildMetadataInfo);
+ metadata = MetadataFactory.ConvertToObjectMetadata(databaseChildMetadataInfo);
}
await requestContext.SendResult(new MetadataQueryResult
diff --git a/src/Microsoft.Kusto.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs b/src/Microsoft.Kusto.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs
index b16677cd..4b48dfa0 100644
--- a/src/Microsoft.Kusto.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs
@@ -38,15 +38,15 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
[Export(typeof(IHostedService))]
public class ObjectExplorerService : HostedService, IComposableService, IHostedService, IDisposable
{
+ private readonly IConnectedBindingQueue _connectedBindingQueue;
internal const string uriPrefix = "objectexplorer://";
// Instance of the connection service, used to get the connection info for a given owner URI
private ConnectionService connectionService;
- private IProtocolEndpoint serviceHost;
+ private IProtocolEndpoint _serviceHost;
private ConcurrentDictionary sessionMap;
private readonly Lazy>> applicableNodeChildFactories;
private IMultiServiceProvider serviceProvider;
- private ConnectedBindingQueue bindingQueue = new ConnectedBindingQueue(needsMetadata: false);
private string connectionName = "ObjectExplorer";
///
@@ -57,34 +57,15 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
///
/// Singleton constructor
///
- public ObjectExplorerService()
+ [ImportingConstructor]
+ public ObjectExplorerService(IConnectedBindingQueue connectedBindingQueue)
{
+ _connectedBindingQueue = connectedBindingQueue;
sessionMap = new ConcurrentDictionary();
- applicableNodeChildFactories = new Lazy>>(() => PopulateFactories());
+ applicableNodeChildFactories = new Lazy>>(PopulateFactories);
NodePathGenerator.Initialize();
}
- internal ConnectedBindingQueue ConnectedBindingQueue
- {
- get
- {
- return bindingQueue;
- }
- set
- {
- this.bindingQueue = value;
- }
- }
-
- ///
- /// Internal for testing only
- ///
- internal ObjectExplorerService(ExtensionServiceProvider serviceProvider)
- : this()
- {
- SetServiceProvider(serviceProvider);
- }
-
private Dictionary> ApplicableNodeChildFactories
{
get
@@ -116,7 +97,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
connectionService = provider.GetService();
try
{
- connectionService.RegisterConnectedQueue(connectionName, bindingQueue);
+ connectionService.RegisterConnectedQueue(connectionName, _connectedBindingQueue);
}
catch(Exception ex)
@@ -132,9 +113,9 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
public override void InitializeService(IProtocolEndpoint serviceHost)
{
Logger.Write(TraceEventType.Verbose, "ObjectExplorer service initialized");
- this.serviceHost = serviceHost;
+ _serviceHost = serviceHost;
- this.ConnectedBindingQueue.OnUnhandledException += OnUnhandledException;
+ _connectedBindingQueue.OnUnhandledException += OnUnhandledException;
// Register handlers for requests
serviceHost.SetRequestHandler(CreateSessionRequest.Type, HandleCreateSessionRequest);
@@ -217,7 +198,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
if (!sessionMap.TryGetValue(uri, out session))
{
Logger.Write(TraceEventType.Verbose, $"Cannot expand object explorer node. Couldn't find session for uri. {uri} ");
- await serviceHost.SendEvent(ExpandCompleteNotification.Type, new ExpandResponse
+ await _serviceHost.SendEvent(ExpandCompleteNotification.Type, new ExpandResponse
{
SessionId = expandParams.SessionId,
NodePath = expandParams.NodePath,
@@ -247,7 +228,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
if (!sessionMap.TryGetValue(uri, out session))
{
Logger.Write(TraceEventType.Verbose, $"Cannot expand object explorer node. Couldn't find session for uri. {uri} ");
- await serviceHost.SendEvent(ExpandCompleteNotification.Type, new ExpandResponse
+ await _serviceHost.SendEvent(ExpandCompleteNotification.Type, new ExpandResponse
{
SessionId = refreshParams.SessionId,
NodePath = refreshParams.NodePath,
@@ -319,7 +300,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
{
if (session != null && session.ConnectionInfo != null)
{
- bindingQueue.RemoveBindigContext(session.ConnectionInfo);
+ _connectedBindingQueue.RemoveBindingContext(session.ConnectionInfo);
}
}
connectionService.Disconnect(new DisconnectParams()
@@ -352,7 +333,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
ErrorMessage = result.Exception != null ? result.Exception.Message : $"Failed to create session for session id {uri}"
};
- await serviceHost.SendEvent(CreateSessionCompleteNotification.Type, response);
+ await _serviceHost.SendEvent(CreateSessionCompleteNotification.Type, response);
}
return result;
}).ContinueWithOnFaulted(null);
@@ -388,7 +369,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
SessionId = uri,
ErrorMessage = session.ErrorMessage
};
- await serviceHost.SendEvent(CreateSessionCompleteNotification.Type, response);
+ await _serviceHost.SendEvent(CreateSessionCompleteNotification.Type, response);
return response;
}
return null;
@@ -426,8 +407,8 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
try
{
int timeout = (int)TimeSpan.FromSeconds(settings?.ExpandTimeout ?? ObjectExplorerSettings.DefaultExpandTimeout).TotalMilliseconds;
- QueueItem queueItem = bindingQueue.QueueBindingOperation(
- key: bindingQueue.AddConnectionContext(session.ConnectionInfo, connectionName),
+ QueueItem queueItem = _connectedBindingQueue.QueueBindingOperation(
+ key: _connectedBindingQueue.AddConnectionContext(session.ConnectionInfo, false, connectionName, false),
bindingTimeout: timeout,
waitForLockTimeout: timeout,
bindOperation: (bindingContext, cancelToken) =>
@@ -502,8 +483,8 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
}
int timeout = (int)TimeSpan.FromSeconds(settings?.CreateSessionTimeout ?? ObjectExplorerSettings.DefaultCreateSessionTimeout).TotalMilliseconds;
- QueueItem queueItem = bindingQueue.QueueBindingOperation(
- key: bindingQueue.AddConnectionContext(connectionInfo, connectionName),
+ QueueItem queueItem = _connectedBindingQueue.QueueBindingOperation(
+ key: _connectedBindingQueue.AddConnectionContext(connectionInfo, false, connectionName),
bindingTimeout: timeout,
waitForLockTimeout: timeout,
bindOperation: (bindingContext, cancelToken) =>
@@ -564,7 +545,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
ErrorMessage = errorMessage,
SessionId = uri
};
- await serviceHost.SendEvent(CreateSessionCompleteNotification.Type, result);
+ await _serviceHost.SendEvent(CreateSessionCompleteNotification.Type, result);
}
internal async Task SendSessionDisconnectedNotification(string uri, bool success, string errorMessage)
@@ -576,7 +557,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
ErrorMessage = errorMessage,
SessionId = uri
};
- await serviceHost.SendEvent(SessionDisconnectedNotification.Type, result);
+ await _serviceHost.SendEvent(SessionDisconnectedNotification.Type, result);
}
private void RunExpandTask(ObjectExplorerSession session, ExpandParams expandParams, bool forceRefresh = false)
@@ -594,7 +575,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
cancellationTokenSource.Cancel();
ExpandResponse response = CreateExpandResponse(session, expandParams);
response.ErrorMessage = result.Exception != null ? result.Exception.Message: $"Failed to expand node: {expandParams.NodePath} in session {session.Uri}";
- await serviceHost.SendEvent(ExpandCompleteNotification.Type, response);
+ await _serviceHost.SendEvent(ExpandCompleteNotification.Type, response);
}
return result;
}).ContinueWithOnFaulted(null);
@@ -636,7 +617,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
}
else
{
- await serviceHost.SendEvent(ExpandCompleteNotification.Type, response);
+ await _serviceHost.SendEvent(ExpandCompleteNotification.Type, response);
}
}
@@ -752,10 +733,10 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
public void Dispose()
{
- if (bindingQueue != null)
+ if (_connectedBindingQueue != null)
{
- bindingQueue.OnUnhandledException -= OnUnhandledException;
- bindingQueue.Dispose();
+ _connectedBindingQueue.OnUnhandledException -= OnUnhandledException;
+ _connectedBindingQueue.Dispose();
}
}
@@ -787,21 +768,12 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
internal class ObjectExplorerSession
{
- private ConnectionService connectionService;
- private IMultiServiceProvider serviceProvider;
-
- // TODO decide whether a cache is needed to handle lookups in elements with a large # children
- //private const int Cachesize = 10000;
- //private Cache cache;
-
- public ObjectExplorerSession(string uri, TreeNode root, IMultiServiceProvider serviceProvider, ConnectionService connectionService)
+ public ObjectExplorerSession(string uri, TreeNode root)
{
Validate.IsNotNullOrEmptyString("uri", uri);
Validate.IsNotNull("root", root);
Uri = uri;
Root = root;
- this.serviceProvider = serviceProvider;
- this.connectionService = connectionService;
}
public string Uri { get; private set; }
@@ -813,13 +785,13 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
public static ObjectExplorerSession CreateSession(ConnectionCompleteParams response, IMultiServiceProvider serviceProvider, ServerConnection serverConnection, IDataSource dataSource, bool isDefaultOrSystemDatabase)
{
- DataSourceObjectMetadata objectMetadata = DataSourceFactory.CreateClusterMetadata(dataSource.ClusterName);
+ DataSourceObjectMetadata objectMetadata = MetadataFactory.CreateClusterMetadata(dataSource.ClusterName);
ServerNode rootNode = new ServerNode(response, serviceProvider, serverConnection, dataSource, objectMetadata);
- var session = new ObjectExplorerSession(response.OwnerUri, rootNode, serviceProvider, serviceProvider.GetService());
+ var session = new ObjectExplorerSession(response.OwnerUri, rootNode);
if (!isDefaultOrSystemDatabase)
{
- DataSourceObjectMetadata databaseMetadata = DataSourceFactory.CreateDatabaseMetadata(objectMetadata, response.ConnectionSummary.DatabaseName);
+ DataSourceObjectMetadata databaseMetadata = MetadataFactory.CreateDatabaseMetadata(objectMetadata, response.ConnectionSummary.DatabaseName);
// Assuming the databases are in a folder under server node
DataSourceTreeNode databaseNode = new DataSourceTreeNode(dataSource, databaseMetadata) {
diff --git a/src/Microsoft.Kusto.ServiceLayer/Program.cs b/src/Microsoft.Kusto.ServiceLayer/Program.cs
index 409a56cd..34abf395 100644
--- a/src/Microsoft.Kusto.ServiceLayer/Program.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/Program.cs
@@ -2,13 +2,10 @@
// 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.Kusto.ServiceLayer.Hosting;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.Kusto.ServiceLayer.Utility;
using Microsoft.SqlTools.Utility;
-using System.IO;
using System.Diagnostics;
-//using SqlToolsContext = Microsoft.SqlTools.ServiceLayer.SqlContext.SqlToolsContext;
namespace Microsoft.Kusto.ServiceLayer
{
diff --git a/src/Microsoft.Kusto.ServiceLayer/QueryExecution/QueryExecutionService.cs b/src/Microsoft.Kusto.ServiceLayer/QueryExecution/QueryExecutionService.cs
index ec0358cb..82eafe0a 100644
--- a/src/Microsoft.Kusto.ServiceLayer/QueryExecution/QueryExecutionService.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/QueryExecution/QueryExecutionService.cs
@@ -16,7 +16,6 @@ using Microsoft.Kusto.ServiceLayer.QueryExecution.DataStorage;
using Microsoft.Kusto.ServiceLayer.SqlContext;
using Microsoft.Kusto.ServiceLayer.Workspace;
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
-using Microsoft.Kusto.ServiceLayer.Hosting;
using Microsoft.SqlTools.Utility;
using System.Diagnostics;
diff --git a/src/Microsoft.Kusto.ServiceLayer/Scripting/IScripter.cs b/src/Microsoft.Kusto.ServiceLayer/Scripting/IScripter.cs
new file mode 100644
index 00000000..eec9584e
--- /dev/null
+++ b/src/Microsoft.Kusto.ServiceLayer/Scripting/IScripter.cs
@@ -0,0 +1,12 @@
+using Microsoft.Kusto.ServiceLayer.DataSource;
+using Microsoft.Kusto.ServiceLayer.Scripting.Contracts;
+using Microsoft.SqlServer.Management.Sdk.Sfc;
+
+namespace Microsoft.Kusto.ServiceLayer.Scripting
+{
+ public interface IScripter
+ {
+ string SelectFromTableOrView(IDataSource dataSource, Urn urn);
+ string AlterFunction(IDataSource dataSource, ScriptingObject scriptingObject);
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/Scripting/ScriptAsScriptingOperation.cs b/src/Microsoft.Kusto.ServiceLayer/Scripting/ScriptAsScriptingOperation.cs
index 4f22ac49..ea29f267 100644
--- a/src/Microsoft.Kusto.ServiceLayer/Scripting/ScriptAsScriptingOperation.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/Scripting/ScriptAsScriptingOperation.cs
@@ -26,6 +26,7 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
///
public class ScriptAsScriptingOperation : SmoScriptingOperation
{
+ private readonly IScripter _scripter;
private static readonly Dictionary scriptCompatibilityMap = LoadScriptCompatibilityMap();
///
/// Left delimiter for an named object
@@ -37,15 +38,11 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
///
public const char RightDelimiter = ']';
- public ScriptAsScriptingOperation(ScriptingParams parameters, IDataSource dataSource): base(parameters)
+ public ScriptAsScriptingOperation(ScriptingParams parameters, string azureAccountToken, IScripter scripter) : base(parameters)
{
- Validate.IsNotNull("dataSource", dataSource);
- DataSource = dataSource;
- }
-
- public ScriptAsScriptingOperation(ScriptingParams parameters, string azureAccountToken) : base(parameters)
- {
- DataSource = DataSourceFactory.Create(DataSourceType.Kusto, this.Parameters.ConnectionString, azureAccountToken);
+ DataSource = DataSourceFactory.Create(DataSourceType.Kusto, this.Parameters.ConnectionString,
+ azureAccountToken);
+ _scripter = scripter;
}
internal IDataSource DataSource { get; set; }
@@ -150,7 +147,7 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
// select from table
if (string.Equals(scriptingObject.Type, "Table", StringComparison.CurrentCultureIgnoreCase))
{
- return new Scripter().SelectFromTableOrView(dataSource, objectUrn);
+ return _scripter.SelectFromTableOrView(dataSource, objectUrn);
}
return string.Empty;
@@ -163,7 +160,7 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
if (string.Equals(scriptingObject.Type, "Function", StringComparison.CurrentCultureIgnoreCase))
{
- return new Scripter().AlterFunction(dataSource, scriptingObject);
+ return _scripter.AlterFunction(dataSource, scriptingObject);
}
return string.Empty;
@@ -177,7 +174,7 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
/// The object name.
/// Whether to schema qualify the object or not
/// The object name, quoted as appropriate and schema-qualified if the option is set
- static private string GenerateSchemaQualifiedName(string schema, string objectName, bool schemaQualify)
+ private static string GenerateSchemaQualifiedName(string schema, string objectName, bool schemaQualify)
{
var qualifiedName = new StringBuilder();
diff --git a/src/Microsoft.Kusto.ServiceLayer/Scripting/Scripter.cs b/src/Microsoft.Kusto.ServiceLayer/Scripting/Scripter.cs
index 3fffc444..827e78fa 100644
--- a/src/Microsoft.Kusto.ServiceLayer/Scripting/Scripter.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/Scripting/Scripter.cs
@@ -3,73 +3,34 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
-using Microsoft.SqlServer.Management.Smo;
-using Microsoft.SqlServer.Management.SqlParser.Intellisense;
-using Microsoft.SqlServer.Management.Common;
+using System.Composition;
+using Microsoft.Kusto.ServiceLayer.Scripting.Contracts;
+using Microsoft.Kusto.ServiceLayer.DataSource;
+using Microsoft.SqlServer.Management.Sdk.Sfc;
+using System.Text;
namespace Microsoft.Kusto.ServiceLayer.Scripting
{
- internal partial class Scripter
+ [Export(typeof(IScripter))]
+ public class Scripter : IScripter
{
- private void Initialize()
+ public string SelectFromTableOrView(IDataSource dataSource, Urn urn)
{
- // Instantiate the mapping dictionaries
+ StringBuilder selectQuery = new StringBuilder();
- // Mapping for supported type
- AddSupportedType(DeclarationType.Table, "Table", "table", typeof(Table));
- AddSupportedType(DeclarationType.View, "View", "view", typeof(View));
- AddSupportedType(DeclarationType.StoredProcedure, "StoredProcedure", "stored procedure", typeof(StoredProcedure));
- AddSupportedType(DeclarationType.Schema, "Schema", "schema", typeof(Schema));
- AddSupportedType(DeclarationType.UserDefinedDataType, "UserDefinedDataType", "user-defined data type", typeof(UserDefinedDataType));
- AddSupportedType(DeclarationType.UserDefinedTableType, "UserDefinedTableType", "user-defined table type", typeof(UserDefinedTableType));
- AddSupportedType(DeclarationType.Synonym, "Synonym", "", typeof(Synonym));
- AddSupportedType(DeclarationType.ScalarValuedFunction, "Function", "scalar-valued function", typeof(UserDefinedFunction));
- AddSupportedType(DeclarationType.TableValuedFunction, "Function", "table-valued function", typeof(UserDefinedFunction));
+ // TODOKusto: Can we combine this with snippets. All queries generated here could also be snippets.
+ // TODOKusto: Extract into the Kusto folder.
+ selectQuery.Append($"{KustoQueryUtils.EscapeName(urn.GetAttribute("Name"))}");
+ selectQuery.Append($"{KustoQueryUtils.StatementSeparator}");
+ selectQuery.Append("limit 1000");
- // Mapping for database engine edition
- targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.Unknown, "SqlServerEnterpriseEdition"); //default case
- targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.Personal, "SqlServerPersonalEdition");
- targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.Standard, "SqlServerStandardEdition");
- targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.Enterprise, "SqlServerEnterpriseEdition");
- targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.Express, "SqlServerExpressEdition");
- targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.SqlDatabase, "SqlAzureDatabaseEdition");
- targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.SqlDataWarehouse, "SqlDatawarehouseEdition");
- targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.SqlStretchDatabase, "SqlServerStretchEdition");
- targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.SqlManagedInstance, "SqlServerManagedInstance");
+ return selectQuery.ToString();
+ }
- // Mapping for database engine type
- serverVersionMap.Add(9, "Script90Compat");
- serverVersionMap.Add(10, "Script100Compat");
- serverVersionMap.Add(11, "Script110Compat");
- serverVersionMap.Add(12, "Script120Compat");
- serverVersionMap.Add(13, "Script140Compat");
- serverVersionMap.Add(14, "Script140Compat");
- serverVersionMap.Add(15, "Script140Compat");
-
- // Mapping the object types for scripting
- objectScriptMap.Add("table", "Table");
- objectScriptMap.Add("view", "View");
- objectScriptMap.Add("function", "Function");
- objectScriptMap.Add("storedprocedure", "Procedure");
- objectScriptMap.Add("userdefinedfunction", "Function");
- objectScriptMap.Add("tablevaluedfunction", "Function");
- objectScriptMap.Add("userdefineddatatype", "Type");
- objectScriptMap.Add("user", "User");
- objectScriptMap.Add("default", "Default");
- objectScriptMap.Add("rule", "Rule");
- objectScriptMap.Add("databaserole", "Role");
- objectScriptMap.Add("applicationrole", "Application Role");
- objectScriptMap.Add("sqlassembly", "Assembly");
- objectScriptMap.Add("ddltrigger", "Trigger");
- objectScriptMap.Add("synonym", "Synonym");
- objectScriptMap.Add("xmlschemacollection", "Xml Schema Collection");
- objectScriptMap.Add("schema", "Schema");
- objectScriptMap.Add("planguide", "sp_create_plan_guide");
- objectScriptMap.Add("userdefinedtype", "Type");
- objectScriptMap.Add("userdefinedaggregate", "Aggregate");
- objectScriptMap.Add("fulltextcatalog", "Fulltext Catalog");
- objectScriptMap.Add("userdefinedtabletype", "Type");
+ public string AlterFunction(IDataSource dataSource, ScriptingObject scriptingObject)
+ {
+ var functionName = scriptingObject.Name.Substring(0, scriptingObject.Name.IndexOf('('));
+ return dataSource.GenerateAlterFunctionScript(functionName);
}
}
-}
-
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/Scripting/ScripterCore.cs b/src/Microsoft.Kusto.ServiceLayer/Scripting/ScripterCore.cs
deleted file mode 100644
index ab1207fe..00000000
--- a/src/Microsoft.Kusto.ServiceLayer/Scripting/ScripterCore.cs
+++ /dev/null
@@ -1,683 +0,0 @@
-//
-// 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.Generic;
-using System.IO;
-using System.Linq;
-using Microsoft.SqlServer.Management.Common;
-using Microsoft.SqlServer.Management.Smo;
-using Microsoft.SqlServer.Management.SqlParser.Intellisense;
-using Microsoft.SqlServer.Management.SqlParser.MetadataProvider;
-using Microsoft.SqlServer.Management.SqlParser.Parser;
-using Microsoft.SqlTools.Hosting.Protocol;
-using Microsoft.Kusto.ServiceLayer.Connection;
-using Microsoft.Kusto.ServiceLayer.LanguageServices;
-using Microsoft.Kusto.ServiceLayer.Utility;
-using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
-using Microsoft.Kusto.ServiceLayer.Scripting.Contracts;
-using Microsoft.Kusto.ServiceLayer.DataSource;
-using Location = Microsoft.Kusto.ServiceLayer.Workspace.Contracts.Location;
-using Microsoft.SqlServer.Management.Sdk.Sfc;
-using System.Text;
-using System.Data;
-using Range = Microsoft.Kusto.ServiceLayer.Workspace.Contracts.Range;
-
-namespace Microsoft.Kusto.ServiceLayer.Scripting
-{
- internal partial class Scripter
- {
- private bool error;
- private string errorMessage;
- private IDataSource DataSource { get; set; }
- private ConnectionInfo connectionInfo;
- private string tempPath;
-
- // Dictionary that holds the object name (as appears on the TSQL create statement)
- private Dictionary sqlObjectTypes = new Dictionary();
-
- private Dictionary sqlObjectTypesFromQuickInfo = new Dictionary();
-
- private Dictionary targetDatabaseEngineEditionMap = new Dictionary();
-
- private Dictionary serverVersionMap = new Dictionary();
-
- private Dictionary objectScriptMap = new Dictionary();
-
- internal Scripter() {}
-
- ///
- /// Initialize a Peek Definition helper object
- ///
- /// Data Source
- internal Scripter(IDataSource dataSource, ConnectionInfo connInfo)
- {
- this.DataSource = dataSource;
- this.connectionInfo = connInfo;
- this.tempPath = FileUtilities.GetPeekDefinitionTempFolder();
- Initialize();
- }
-
- ///
- /// Add the given type, scriptgetter and the typeName string to the respective dictionaries
- ///
- private void AddSupportedType(DeclarationType type, string typeName, string quickInfoType, Type smoObjectType)
- {
- sqlObjectTypes.Add(type, typeName);
- if (!string.IsNullOrEmpty(quickInfoType))
- {
- sqlObjectTypesFromQuickInfo.Add(quickInfoType.ToLowerInvariant(), typeName);
- }
- }
-
- ///
- /// Get the script of the selected token based on the type of the token
- ///
- ///
- ///
- ///
- /// Location object of the script file
- internal DefinitionResult GetScript(ParseResult parseResult, Position position, IMetadataDisplayInfoProvider metadataDisplayInfoProvider, string tokenText, string schemaName)
- {
- int parserLine = position.Line;
- int parserColumn = position.Character;
- // Get DeclarationItems from The Intellisense Resolver for the selected token. The type of the selected token is extracted from the declarationItem.
- IEnumerable declarationItems = GetCompletionsForToken(parseResult, parserLine, parserColumn, metadataDisplayInfoProvider);
- if (declarationItems != null && declarationItems.Count() > 0)
- {
- foreach (Declaration declarationItem in declarationItems)
- {
- if (declarationItem.Title == null)
- {
- continue;
- }
-
- StringComparison caseSensitivity = StringComparison.OrdinalIgnoreCase;
- // if declarationItem matches the selected token, script SMO using that type
-
- if (declarationItem.Title.Equals(tokenText, caseSensitivity))
- {
- return GetDefinitionUsingDeclarationType(declarationItem.Type, declarationItem.DatabaseQualifiedName, tokenText, schemaName);
- }
- }
- }
- else
- {
- // if no declarationItem matched the selected token, we try to find the type of the token using QuickInfo.Text
- string quickInfoText = GetQuickInfoForToken(parseResult, parserLine, parserColumn, metadataDisplayInfoProvider);
- return GetDefinitionUsingQuickInfoText(quickInfoText, tokenText, schemaName);
- }
- // no definition found
- return GetDefinitionErrorResult(SR.PeekDefinitionNoResultsError);
- }
-
- ///
- /// Script an object using the type extracted from quickInfo Text
- ///
- /// the text from the quickInfo for the selected token
- /// The text of the selected token
- /// Schema name
- ///
- internal DefinitionResult GetDefinitionUsingQuickInfoText(string quickInfoText, string tokenText, string schemaName)
- {
- StringComparison caseSensitivity = StringComparison.OrdinalIgnoreCase;
- string tokenType = GetTokenTypeFromQuickInfo(quickInfoText, tokenText, caseSensitivity);
- if (tokenType != null)
- {
- if (sqlObjectTypesFromQuickInfo.ContainsKey(tokenType.ToLowerInvariant()))
- {
- // With SqlLogin authentication, the defaultSchema property throws an Exception when accessed.
- // This workaround ensures that a schema name is present by attempting
- // to get the schema name from the declaration item.
- // If all fails, the default schema name is assumed to be "dbo"
- if ((connectionInfo != null && connectionInfo.ConnectionDetails.AuthenticationType.Equals(Constants.SqlLoginAuthenticationType)) && string.IsNullOrEmpty(schemaName))
- {
- string fullObjectName = this.GetFullObjectNameFromQuickInfo(quickInfoText, tokenText, caseSensitivity);
- schemaName = this.GetSchemaFromDatabaseQualifiedName(fullObjectName, tokenText);
- }
- Location[] locations = GetSqlObjectDefinition(
- tokenText,
- schemaName,
- sqlObjectTypesFromQuickInfo[tokenType.ToLowerInvariant()]
- );
- DefinitionResult result = new DefinitionResult
- {
- IsErrorResult = this.error,
- Message = this.errorMessage,
- Locations = locations
- };
- return result;
- }
- else
- {
- // If a type was found but is not in sqlScriptGettersFromQuickInfo, then the type is not supported
- return GetDefinitionErrorResult(SR.PeekDefinitionTypeNotSupportedError);
- }
- }
- // no definition found
- return GetDefinitionErrorResult(SR.PeekDefinitionNoResultsError);
- }
-
- ///
- /// Script a object using the type extracted from declarationItem
- ///
- /// The Declaration object that matched with the selected token
- /// The text of the selected token
- /// Schema name
- ///
- internal DefinitionResult GetDefinitionUsingDeclarationType(DeclarationType type, string databaseQualifiedName, string tokenText, string schemaName)
- {
- if (sqlObjectTypes.ContainsKey(type))
- {
- // With SqlLogin authentication, the defaultSchema property throws an Exception when accessed.
- // This workaround ensures that a schema name is present by attempting
- // to get the schema name from the declaration item.
- // If all fails, the default schema name is assumed to be "dbo"
- if ((connectionInfo != null && connectionInfo.ConnectionDetails.AuthenticationType.Equals(Constants.SqlLoginAuthenticationType)) && string.IsNullOrEmpty(schemaName))
- {
- string fullObjectName = databaseQualifiedName;
- schemaName = this.GetSchemaFromDatabaseQualifiedName(fullObjectName, tokenText);
- }
- Location[] locations = GetSqlObjectDefinition(
- tokenText,
- schemaName,
- sqlObjectTypes[type]
- );
- DefinitionResult result = new DefinitionResult
- {
- IsErrorResult = this.error,
- Message = this.errorMessage,
- Locations = locations
- };
- return result;
- }
- // If a type was found but is not in sqlScriptGetters, then the type is not supported
- return GetDefinitionErrorResult(SR.PeekDefinitionTypeNotSupportedError);
- }
-
- ///
- /// Script a object using SMO and write to a file.
- ///
- /// Function that returns the SMO scripts for an object
- /// SQL object name
- /// Schema name or null
- /// Type of SQL object
- /// Location object representing URI and range of the script file
- internal Location[] GetSqlObjectDefinition(
- string objectName,
- string schemaName,
- string objectType)
- {
- // script file destination
- string tempFileName = (schemaName != null) ? Path.Combine(this.tempPath, string.Format("{0}.{1}.sql", schemaName, objectName))
- : Path.Combine(this.tempPath, string.Format("{0}.sql", objectName));
-
- SmoScriptingOperation operation = InitScriptOperation(objectName, schemaName, objectType);
- operation.Execute();
- string script = operation.ScriptText;
-
- bool objectFound = false;
- int createStatementLineNumber = 0;
-
- File.WriteAllText(tempFileName, script);
- string[] lines = File.ReadAllLines(tempFileName);
- int lineCount = 0;
- string createSyntax = null;
- if (objectScriptMap.ContainsKey(objectType.ToLower()))
- {
- createSyntax = string.Format("CREATE");
- foreach (string line in lines)
- {
- if (LineContainsObject(line, objectName, createSyntax))
- {
- createStatementLineNumber = lineCount;
- objectFound = true;
- break;
- }
- lineCount++;
- }
- }
- if (objectFound)
- {
- Location[] locations = GetLocationFromFile(tempFileName, createStatementLineNumber);
- return locations;
- }
- else
- {
- this.error = true;
- this.errorMessage = SR.PeekDefinitionNoResultsError;
- return null;
- }
- }
-
- #region Helper Methods
- ///
- /// Return schema name from the full name of the database. If schema is missing return dbo as schema name.
- ///
- /// The full database qualified name(database.schema.object)
- /// Object name
- /// Schema name
- internal string GetSchemaFromDatabaseQualifiedName(string fullObjectName, string objectName)
- {
- if (!string.IsNullOrEmpty(fullObjectName))
- {
- string[] tokens = fullObjectName.Split('.');
- for (int i = tokens.Length - 1; i > 0; i--)
- {
- if (tokens[i].Equals(objectName))
- {
- return tokens[i - 1];
- }
- }
- }
- return "dbo";
- }
-
- ///
- /// Convert a file to a location array containing a location object as expected by the extension
- ///
- internal Location[] GetLocationFromFile(string tempFileName, int lineNumber)
- {
- // Get absolute Uri based on uri format. This works around a dotnetcore URI bug for linux paths.
- if (Path.DirectorySeparatorChar.Equals('/'))
- {
- tempFileName = "file:" + tempFileName;
- }
- else
- {
- tempFileName = new Uri(tempFileName).AbsoluteUri;
- }
- // Create a location array containing the tempFile Uri, as expected by VSCode.
- Location[] locations = new[]
- {
- new Location
- {
- Uri = tempFileName,
- Range = new Range
- {
- Start = new Position { Line = lineNumber, Character = 0},
- End = new Position { Line = lineNumber + 1, Character = 0}
- }
- }
- };
- return locations;
- }
-
- ///
- /// Helper method to create definition error result object
- ///
- /// Error message
- /// DefinitionResult
- internal DefinitionResult GetDefinitionErrorResult(string errorMessage)
- {
- return new DefinitionResult
- {
- IsErrorResult = true,
- Message = errorMessage,
- Locations = null
- };
- }
-
- ///
- /// Return full object name(database.schema.objectName) from the quickInfo text("type database.schema.objectName")
- ///
- /// QuickInfo Text for this token
- /// Token Text
- /// StringComparison enum
- ///
- internal string GetFullObjectNameFromQuickInfo(string quickInfoText, string tokenText, StringComparison caseSensitivity)
- {
- if (string.IsNullOrEmpty(quickInfoText) || string.IsNullOrEmpty(tokenText))
- {
- return null;
- }
- // extract full object name from quickInfo text
- string[] tokens = quickInfoText.Split(' ');
- List tokenList = tokens.Where(el => el.IndexOf(tokenText, caseSensitivity) >= 0).ToList();
- return (tokenList?.Count() > 0) ? tokenList[0] : null;
- }
-
- ///
- /// Return token type from the quickInfo text("type database.schema.objectName")
- ///
- /// QuickInfo Text for this token
- ///
- /// StringComparison enum
- ///
- internal string GetTokenTypeFromQuickInfo(string quickInfoText, string tokenText, StringComparison caseSensitivity)
- {
- if (string.IsNullOrEmpty(quickInfoText) || string.IsNullOrEmpty(tokenText))
- {
- return null;
- }
- // extract string denoting the token type from quickInfo text
- string[] tokens = quickInfoText.Split(' ');
- List indexList = tokens.Select((s, i) => new { i, s }).Where(el => (el.s).IndexOf(tokenText, caseSensitivity) >= 0).Select(el => el.i).ToList();
- return (indexList?.Count() > 0) ? String.Join(" ", tokens.Take(indexList[0])) : null;
- }
-
-
- ///
- /// Wrapper method that calls Resolver.GetQuickInfo
- ///
- internal string GetQuickInfoForToken(ParseResult parseResult, int parserLine, int parserColumn, IMetadataDisplayInfoProvider metadataDisplayInfoProvider)
- {
- if (parseResult == null || metadataDisplayInfoProvider == null)
- {
- return null;
- }
- Babel.CodeObjectQuickInfo quickInfo = Resolver.GetQuickInfo(
- parseResult, parserLine, parserColumn, metadataDisplayInfoProvider);
- return quickInfo?.Text;
- }
-
- ///
- /// Wrapper method that calls Resolver.FindCompletions
- ///
- ///
- ///
- ///
- ///
- ///
- internal IEnumerable GetCompletionsForToken(ParseResult parseResult, int parserLine, int parserColumn, IMetadataDisplayInfoProvider metadataDisplayInfoProvider)
- {
- if (parseResult == null || metadataDisplayInfoProvider == null)
- {
- return null;
- }
- return Resolver.FindCompletions(
- parseResult, parserLine, parserColumn, metadataDisplayInfoProvider);
- }
-
- ///
- /// Wrapper method that calls Resolver.FindCompletions
- ///
- ///
- ///
- ///
- ///
- ///
- internal SmoScriptingOperation InitScriptOperation(string objectName, string schemaName, string objectType)
- {
- // object that has to be scripted
- ScriptingObject scriptingObject = new ScriptingObject
- {
- Name = objectName,
- Schema = schemaName,
- Type = objectType
- };
-
- // scripting options
- ScriptOptions options = new ScriptOptions
- {
- ScriptCreateDrop = "ScriptCreate",
- TypeOfDataToScript = "SchemaOnly",
- ScriptStatistics = "ScriptStatsNone",
- ScriptExtendedProperties = false,
- ScriptUseDatabase = false,
- IncludeIfNotExists = false,
- GenerateScriptForDependentObjects = false,
- IncludeDescriptiveHeaders = false,
- ScriptCheckConstraints = false,
- ScriptChangeTracking = false,
- ScriptDataCompressionOptions = false,
- ScriptForeignKeys = false,
- ScriptFullTextIndexes = false,
- ScriptIndexes = false,
- ScriptPrimaryKeys = false,
- ScriptTriggers = false,
- UniqueKeys = false
-
- };
-
- List objectList = new List();
- objectList.Add(scriptingObject);
-
- // create parameters for the scripting operation
-
- ScriptingParams parameters = new ScriptingParams
- {
- ConnectionString = ConnectionService.BuildConnectionString(this.connectionInfo.ConnectionDetails),
- ScriptingObjects = objectList,
- ScriptOptions = options,
- ScriptDestination = "ToEditor"
- };
-
- return new ScriptAsScriptingOperation(parameters, DataSource);
- }
-
- internal bool LineContainsObject(string line, string objectName, string createSyntax)
- {
- if (line.IndexOf(createSyntax, StringComparison.OrdinalIgnoreCase) >= 0 &&
- line.IndexOf(objectName, StringComparison.OrdinalIgnoreCase) >=0)
- {
- return true;
- }
- return false;
- }
-
- internal static class ScriptingGlobals
- {
- ///
- /// Left delimiter for an named object
- ///
- public const char LeftDelimiter = '[';
-
- ///
- /// right delimiter for a named object
- ///
- public const char RightDelimiter = ']';
- }
-
- internal static class ScriptingUtils
- {
- ///
- /// Quote the name of a given sql object.
- ///
- /// object
- /// quoted object name
- internal static string QuoteObjectName(string sqlObject)
- {
- return QuoteObjectName(sqlObject, ']');
- }
-
- ///
- /// Quotes the name of a given sql object
- ///
- /// object
- /// quote to use
- ///
- internal static string QuoteObjectName(string sqlObject, char quote)
- {
- int len = sqlObject.Length;
- StringBuilder result = new StringBuilder(sqlObject.Length);
- for (int i = 0; i < len; i++)
- {
- if (sqlObject[i] == quote)
- {
- result.Append(quote);
- }
- result.Append(sqlObject[i]);
- }
- return result.ToString();
- }
- }
-
- internal static string SelectAllValuesFromTransmissionQueue(Urn urn)
- {
- string script = string.Empty;
- StringBuilder selectQuery = new StringBuilder();
-
- /*
- SELECT TOP *, casted_message_body =
- CASE MESSAGE_TYPE_NAME WHEN 'X'
- THEN CAST(MESSAGE_BODY AS NVARCHAR(MAX))
- ELSE MESSAGE_BODY
- END
- FROM [new].[sys].[transmission_queue]
- */
- selectQuery.Append("SELECT TOP (1000) ");
- selectQuery.Append("*, casted_message_body = \r\nCASE message_type_name WHEN 'X' \r\n THEN CAST(message_body AS NVARCHAR(MAX)) \r\n ELSE message_body \r\nEND \r\n");
-
- // from clause
- selectQuery.Append("FROM ");
- Urn dbUrn = urn;
-
- // database
- while (dbUrn.Parent != null && dbUrn.Type != "Database")
- {
- dbUrn = dbUrn.Parent;
- }
- selectQuery.AppendFormat("{0}{1}{2}",
- ScriptingGlobals.LeftDelimiter,
- ScriptingUtils.QuoteObjectName(dbUrn.GetAttribute("Name"), ScriptingGlobals.RightDelimiter),
- ScriptingGlobals.RightDelimiter);
- //SYS
- selectQuery.AppendFormat(".{0}sys{1}",
- ScriptingGlobals.LeftDelimiter,
- ScriptingGlobals.RightDelimiter);
- //TRANSMISSION QUEUE
- selectQuery.AppendFormat(".{0}transmission_queue{1}",
- ScriptingGlobals.LeftDelimiter,
- ScriptingGlobals.RightDelimiter);
-
- script = selectQuery.ToString();
- return script;
- }
-
- internal static string SelectAllValues(Urn urn)
- {
- string script = string.Empty;
- StringBuilder selectQuery = new StringBuilder();
- selectQuery.Append("SELECT TOP (1000) ");
- selectQuery.Append("*, casted_message_body = \r\nCASE message_type_name WHEN 'X' \r\n THEN CAST(message_body AS NVARCHAR(MAX)) \r\n ELSE message_body \r\nEND \r\n");
-
- // from clause
- selectQuery.Append("FROM ");
- Urn dbUrn = urn;
-
- // database
- while (dbUrn.Parent != null && dbUrn.Type != "Database")
- {
- dbUrn = dbUrn.Parent;
- }
- selectQuery.AppendFormat("{0}{1}{2}",
- ScriptingGlobals.LeftDelimiter,
- ScriptingUtils.QuoteObjectName(dbUrn.GetAttribute("Name"), ScriptingGlobals.RightDelimiter),
- ScriptingGlobals.RightDelimiter);
- // schema
- selectQuery.AppendFormat(".{0}{1}{2}",
- ScriptingGlobals.LeftDelimiter,
- ScriptingUtils.QuoteObjectName(urn.GetAttribute("Schema"), ScriptingGlobals.RightDelimiter),
- ScriptingGlobals.RightDelimiter);
- // object
- selectQuery.AppendFormat(".{0}{1}{2}",
- ScriptingGlobals.LeftDelimiter,
- ScriptingUtils.QuoteObjectName(urn.GetAttribute("Name"), ScriptingGlobals.RightDelimiter),
- ScriptingGlobals.RightDelimiter);
-
- //Adding no lock in the end.
- selectQuery.AppendFormat(" WITH(NOLOCK)");
-
- script = selectQuery.ToString();
- return script;
- }
-
- internal DataTable GetColumnNames(Server server, Urn urn, bool isDw)
- {
- List filterExpressions = new List();
- if (server.Version.Major >= 10)
- {
- // We don't have to include sparce columns as all the sparce columns data.
- // Can be obtain from column set columns.
- filterExpressions.Add("@IsSparse=0");
- }
-
- // Check if we're called for EDIT for SQL2016+/Sterling+.
- // We need to omit temporal columns if such are present on this table.
- if (server.Version.Major >= 13 || (DatabaseEngineType.SqlAzureDatabase == server.DatabaseEngineType && server.Version.Major >= 12))
- {
- // We're called in order to generate a list of columns for EDIT TOP N rows.
- // Don't return auto-generated, auto-populated, read-only temporal columns.
- filterExpressions.Add("@GeneratedAlwaysType=0");
- }
-
- // Check if we're called for SQL2017/Sterling+.
- // We need to omit graph internal columns if such are present on this table.
- if (server.Version.Major >= 14 || (DatabaseEngineType.SqlAzureDatabase == server.DatabaseEngineType && !isDw))
- {
- // from Smo.GraphType:
- // 0 = None
- // 1 = GraphId
- // 2 = GraphIdComputed
- // 3 = GraphFromId
- // 4 = GraphFromObjId
- // 5 = GraphFromIdComputed
- // 6 = GraphToId
- // 7 = GraphToObjId
- // 8 = GraphToIdComputed
- //
- // We only want to show types 0, 2, 5, and 8:
- filterExpressions.Add("(@GraphType=0 or @GraphType=2 or @GraphType=5 or @GraphType=8)");
- }
-
- Request request = new Request();
- // If we have any filters on the columns, add them.
- if (filterExpressions.Count > 0)
- {
- request.Urn = String.Format("{0}/Column[{1}]", urn.ToString(), string.Join(" and ", filterExpressions.ToArray()));
- }
- else
- {
- request.Urn = String.Format("{0}/Column", urn.ToString());
- }
-
- request.Fields = new String[] { "Name" };
-
- // get the columns in the order they were created
- OrderBy order = new OrderBy();
- order.Dir = OrderBy.Direction.Asc;
- order.Field = "ID";
- request.OrderByList = new OrderBy[] { order };
-
- Enumerator en = new Enumerator();
-
- // perform the query.
- DataTable dt = null;
- EnumResult result = en.Process(server.ConnectionContext, request);
-
- if (result.Type == ResultType.DataTable)
- {
- dt = result;
- }
- else
- {
- dt = ((DataSet)result).Tables[0];
- }
- return dt;
- }
-
- internal string SelectFromTableOrView(IDataSource dataSource, Urn urn)
- {
- StringBuilder selectQuery = new StringBuilder();
-
- // TODOKusto: Can we combine this with snippets. All queries generated here could also be snippets.
- // TODOKusto: Extract into the Kusto folder.
- selectQuery.Append($"{KustoQueryUtils.EscapeName(urn.GetAttribute("Name"))}");
- selectQuery.Append($"{KustoQueryUtils.StatementSeparator}");
- selectQuery.Append("limit 1000");
-
- return selectQuery.ToString();
- }
-
- internal string AlterFunction(IDataSource dataSource, ScriptingObject scriptingObject)
- {
- var functionName = scriptingObject.Name.Substring(0, scriptingObject.Name.IndexOf('('));
- return dataSource.GenerateAlterFunctionScript(functionName);
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/Scripting/ScriptingScriptOperation.cs b/src/Microsoft.Kusto.ServiceLayer/Scripting/ScriptingScriptOperation.cs
index de0fedc1..119838a4 100644
--- a/src/Microsoft.Kusto.ServiceLayer/Scripting/ScriptingScriptOperation.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/Scripting/ScriptingScriptOperation.cs
@@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
+using Microsoft.Kusto.ServiceLayer.DataSource;
using Microsoft.SqlServer.Management.SqlScriptPublish;
using Microsoft.Kusto.ServiceLayer.Scripting.Contracts;
using Microsoft.SqlTools.Utility;
@@ -28,7 +29,7 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
private string azureAccessToken;
- public ScriptingScriptOperation(ScriptingParams parameters, string azureAccessToken): base(parameters)
+ public ScriptingScriptOperation(ScriptingParams parameters, string azureAccessToken) : base(parameters)
{
this.azureAccessToken = azureAccessToken;
}
diff --git a/src/Microsoft.Kusto.ServiceLayer/Scripting/ScriptingService.cs b/src/Microsoft.Kusto.ServiceLayer/Scripting/ScriptingService.cs
index 97b391b9..76cf725e 100644
--- a/src/Microsoft.Kusto.ServiceLayer/Scripting/ScriptingService.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/Scripting/ScriptingService.cs
@@ -10,7 +10,7 @@ using System.Threading.Tasks;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.Kusto.ServiceLayer.Connection;
-using Microsoft.Kusto.ServiceLayer.Hosting;
+using Microsoft.Kusto.ServiceLayer.DataSource;
using Microsoft.Kusto.ServiceLayer.Scripting.Contracts;
using Microsoft.SqlTools.Utility;
using Microsoft.Kusto.ServiceLayer.Utility;
@@ -21,19 +21,21 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
/// Main class for Scripting Service functionality
///
public sealed class ScriptingService : IDisposable
- {
+ {
private const int ScriptingOperationTimeout = 60000;
private static readonly Lazy LazyInstance = new Lazy(() => new ScriptingService());
public static ScriptingService Instance => LazyInstance.Value;
- private static ConnectionService connectionService = null;
+ private static ConnectionService connectionService;
private readonly Lazy> operations =
new Lazy>(() => new ConcurrentDictionary());
private bool disposed;
+
+ private IScripter _scripter;
///
/// Internal for testing purposes only
@@ -64,8 +66,9 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
///
///
///
- public void InitializeService(ServiceHost serviceHost)
+ public void InitializeService(ServiceHost serviceHost, IScripter scripter)
{
+ _scripter = scripter;
serviceHost.SetRequestHandler(ScriptingRequest.Type, this.HandleScriptExecuteRequest);
serviceHost.SetRequestHandler(ScriptingCancelRequest.Type, this.HandleScriptCancelRequest);
serviceHost.SetRequestHandler(ScriptingListObjectsRequest.Type, this.HandleListObjectsRequest);
@@ -132,7 +135,7 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
}
else
{
- operation = new ScriptAsScriptingOperation(parameters, accessToken);
+ operation = new ScriptAsScriptingOperation(parameters, accessToken, _scripter);
}
operation.PlanNotification += (sender, e) => requestContext.SendEvent(ScriptingPlanNotificationEvent.Type, e).Wait();
diff --git a/src/Microsoft.Kusto.ServiceLayer/Scripting/SmoScriptingOperation.cs b/src/Microsoft.Kusto.ServiceLayer/Scripting/SmoScriptingOperation.cs
index ede7f1db..de880dcb 100644
--- a/src/Microsoft.Kusto.ServiceLayer/Scripting/SmoScriptingOperation.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/Scripting/SmoScriptingOperation.cs
@@ -22,7 +22,7 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
///
public abstract class SmoScriptingOperation : ScriptingOperation
{
- private bool disposed = false;
+ private bool _disposed;
public SmoScriptingOperation(ScriptingParams parameters)
{
@@ -177,10 +177,10 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
///
public override void Dispose()
{
- if (!disposed)
+ if (!_disposed)
{
this.Cancel();
- disposed = true;
+ _disposed = true;
}
}
diff --git a/src/Microsoft.Kusto.ServiceLayer/ServiceHost.cs b/src/Microsoft.Kusto.ServiceLayer/ServiceHost.cs
index a5b4dafe..d6f66dc4 100644
--- a/src/Microsoft.Kusto.ServiceLayer/ServiceHost.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/ServiceHost.cs
@@ -5,20 +5,20 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
+using Microsoft.Kusto.ServiceLayer.Connection;
+using Microsoft.Kusto.ServiceLayer.Utility;
using Microsoft.SqlTools.Extensibility;
using Microsoft.SqlTools.Hosting;
using Microsoft.SqlTools.Hosting.Contracts;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.Hosting.Protocol.Channel;
using Microsoft.SqlTools.Utility;
-using Microsoft.Kusto.ServiceLayer.Connection;
-using Microsoft.Kusto.ServiceLayer.Utility;
-using System.Diagnostics;
-namespace Microsoft.Kusto.ServiceLayer.Hosting
+namespace Microsoft.Kusto.ServiceLayer
{
///
/// SQL Tools VS Code Language Server request handler. Provides the entire JSON RPC
diff --git a/src/Microsoft.Kusto.ServiceLayer/Workspace/WorkspaceService.cs b/src/Microsoft.Kusto.ServiceLayer/Workspace/WorkspaceService.cs
index 6bcb09c7..b0e6a9c9 100644
--- a/src/Microsoft.Kusto.ServiceLayer/Workspace/WorkspaceService.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/Workspace/WorkspaceService.cs
@@ -10,7 +10,6 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.SqlTools.Hosting.Protocol;
-using Microsoft.Kusto.ServiceLayer.Hosting;
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
using Microsoft.SqlTools.Utility;
using Range = Microsoft.Kusto.ServiceLayer.Workspace.Contracts.Range;
diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs
index b62e7bb7..7ba674ea 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs
@@ -73,15 +73,6 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
{
this.bindingQueue = value;
}
- }
-
- ///
- /// Internal for testing only
- ///
- internal ObjectExplorerService(ExtensionServiceProvider serviceProvider)
- : this()
- {
- SetServiceProvider(serviceProvider);
}
private Dictionary> ApplicableNodeChildFactories