Clean-up the autocomplete SMO integration.

This commit is contained in:
Karl Burtram
2016-09-01 00:23:39 -07:00
parent 013498fc3d
commit 1332fd112e
11 changed files with 421 additions and 516 deletions

View File

@@ -22,6 +22,7 @@ using Microsoft.SqlServer.Management.SqlParser.Parser;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
using Microsoft.SqlTools.ServiceLayer.Test.QueryExecution;
using Microsoft.SqlTools.ServiceLayer.Test.Utility;
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
using Microsoft.SqlTools.Test.Utility;
@@ -38,91 +39,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
{
#region "Diagnostics tests"
// [Fact]
// public void TestParseWideWorldImporters()
// {
// var sql = File.ReadAllText(@"e:\data\script.sql");
// //string sql = @"SELECT ";
// ParseOptions parseOptions = new ParseOptions();
// ParseResult parseResult = Parser.IncrementalParse(
// sql,
// null,
// parseOptions);
// }
// [Fact]
// public void TestSmo()
// {
// SqlConnectionStringBuilder connectionBuilder = new SqlConnectionStringBuilder();
// connectionBuilder["Data Source"] = "sqltools11";
// connectionBuilder["Integrated Security"] = false;
// connectionBuilder["User Id"] = "sa";
// connectionBuilder["Password"] = "Yukon900";
// connectionBuilder["Initial Catalog"] = "master";
// string connectionString = connectionBuilder.ToString();
// var conn = new SqlConnection(connectionString);
// var sqlConn = new ServerConnection(conn);
// var server = new Server(sqlConn);
// string s = "";
// foreach (Database db2 in server.Databases)
// {
// s += db2.Name;
// }
// var metadata = SmoMetadataProvider.CreateConnectedProvider(sqlConn);
// var db = metadata.Server.Databases["master"];
// }
// [Fact]
// public void TestSmoMetadataProvider()
// {
// SqlConnectionStringBuilder connectionBuilder = new SqlConnectionStringBuilder();
// //connectionBuilder["Data Source"] = "sqltools11";
// connectionBuilder["Data Source"] = "localhost";
// connectionBuilder["Integrated Security"] = false;
// connectionBuilder["User Id"] = "sa";
// connectionBuilder["Password"] = "Yukon900";
// connectionBuilder["Initial Catalog"] = "master";
// try
// {
// var sqlConnection = new SqlConnection(connectionBuilder.ToString());
// var connection = new ServerConnection(sqlConnection);
// var metadataProvider = SmoMetadataProvider.CreateConnectedProvider(connection);
// var binder = BinderProvider.CreateBinder(metadataProvider);
// var displayInfoProvider = new MetadataDisplayInfoProvider();
// //string sql = @"SELECT * FROM sys.objects;";
// string sql = @"SELECT ";
// ParseOptions parseOptions = new ParseOptions();
// ParseResult parseResult = Parser.IncrementalParse(
// sql,
// null,
// parseOptions);
// List<ParseResult> parseResults = new List<ParseResult>();
// parseResults.Add(parseResult);
// binder.Bind(parseResults, "master", BindMode.Batch);
// var comp = Resolver.FindCompletions(parseResult, 1, 8, displayInfoProvider);
// comp.Add(null);
// }
// finally
// {
// // Check if we failed to create a binder object. If so, we temporarely
// // use a no-op binder which has the effect of turning off binding. We
// // also set a timer that after the specified timeout expires removes
// // the no-op timer (object becomes dead) which would give clients of
// // this class an opportunity to remove it and create a new one.
// }
// }
/// <summary>
/// Verify that the latest SqlParser (2016 as of this writing) is used by default
/// </summary>
@@ -234,6 +150,29 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
#region "Autocomplete Tests"
// This test currently requires a live database connection to initialize
// SMO connected metadata provider. Since we don't want a live DB dependency
// in the CI unit tests this scenario is currently disabled.
//[Fact]
public void AutoCompleteFindCompletions()
{
TextDocumentPosition textDocument;
ConnectionInfo connInfo;
ScriptFile scriptFile;
Common.GetAutoCompleteTestObjects(out textDocument, out scriptFile, out connInfo);
textDocument.Position.Character = 7;
scriptFile.Contents = "select ";
var autoCompleteService = AutoCompleteService.Instance;
var completions = autoCompleteService.GetCompletionItems(
textDocument,
scriptFile,
connInfo);
Assert.True(completions.Length > 0);
}
/// <summary>
/// Creates a mock db command that returns a predefined result set
/// </summary>
@@ -261,166 +200,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
return connectionMock.Object;
}
#if false
/// <summary>
/// Verify that the autocomplete service returns tables for the current connection as suggestions
/// </summary>
[Fact]
public void TablesAreReturnedAsAutocompleteSuggestions()
{
// Result set for the query of database tables
Dictionary<string, string>[] data =
{
new Dictionary<string, string> { {"name", "master" } },
new Dictionary<string, string> { {"name", "model" } }
};
var mockFactory = new Mock<ISqlConnectionFactory>();
mockFactory.Setup(factory => factory.CreateSqlConnection(It.IsAny<string>()))
.Returns(CreateMockDbConnection(new[] {data}));
var connectionService = new ConnectionService(mockFactory.Object);
var autocompleteService = new AutoCompleteService();
autocompleteService.ConnectionServiceInstance = connectionService;
autocompleteService.InitializeService(Microsoft.SqlTools.ServiceLayer.Hosting.ServiceHost.Instance);
// Open a connection
// The cache should get updated as part of this
ConnectParams connectionRequest = TestObjects.GetTestConnectionParams();
var connectionResult = connectionService.Connect(connectionRequest);
Assert.NotEmpty(connectionResult.ConnectionId);
// Check that there is one cache created in the auto complete service
Assert.Equal(1, autocompleteService.GetCacheCount());
// Check that we get table suggestions for an autocomplete request
TextDocumentPosition position = new TextDocumentPosition();
position.TextDocument = new TextDocumentIdentifier();
position.TextDocument.Uri = connectionRequest.OwnerUri;
position.Position = new Position();
position.Position.Line = 1;
position.Position.Character = 1;
var items = autocompleteService.GetCompletionItems(position);
Assert.Equal(2, items.Length);
Assert.Equal("master", items[0].Label);
Assert.Equal("model", items[1].Label);
}
#endif
/// <summary>
/// Verify that only one intellisense cache is created for two documents using
/// the autocomplete service when they share a common connection.
/// </summary>
[Fact]
public void OnlyOneCacheIsCreatedForTwoDocumentsWithSameConnection()
{
var connectionService = new ConnectionService(TestObjects.GetTestSqlConnectionFactory());
var autocompleteService = new AutoCompleteService();
autocompleteService.ConnectionServiceInstance = connectionService;
autocompleteService.InitializeService(Microsoft.SqlTools.ServiceLayer.Hosting.ServiceHost.Instance);
// Open two connections
ConnectParams connectionRequest1 = TestObjects.GetTestConnectionParams();
connectionRequest1.OwnerUri = "file:///my/first/file.sql";
ConnectParams connectionRequest2 = TestObjects.GetTestConnectionParams();
connectionRequest2.OwnerUri = "file:///my/second/file.sql";
var connectionResult1 = connectionService.Connect(connectionRequest1);
Assert.NotEmpty(connectionResult1.ConnectionId);
var connectionResult2 = connectionService.Connect(connectionRequest2);
Assert.NotEmpty(connectionResult2.ConnectionId);
// Verify that only one intellisense cache is created to service both URI's
Assert.Equal(1, autocompleteService.GetCacheCount());
}
#if false
/// <summary>
/// Verify that two different intellisense caches and corresponding autocomplete
/// suggestions are provided for two documents with different connections.
/// </summary>
[Fact]
public void TwoCachesAreCreatedForTwoDocumentsWithDifferentConnections()
{
const string testDb1 = "my_db";
const string testDb2 = "my_other_db";
// Result set for the query of database tables
Dictionary<string, string>[] data1 =
{
new Dictionary<string, string> { {"name", "master" } },
new Dictionary<string, string> { {"name", "model" } }
};
Dictionary<string, string>[] data2 =
{
new Dictionary<string, string> { {"name", "master" } },
new Dictionary<string, string> { {"name", "my_table" } },
new Dictionary<string, string> { {"name", "my_other_table" } }
};
var mockFactory = new Mock<ISqlConnectionFactory>();
mockFactory.Setup(factory => factory.CreateSqlConnection(It.Is<string>(x => x.Contains(testDb1))))
.Returns(CreateMockDbConnection(new[] {data1}));
mockFactory.Setup(factory => factory.CreateSqlConnection(It.Is<string>(x => x.Contains(testDb2))))
.Returns(CreateMockDbConnection(new[] {data2}));
var connectionService = new ConnectionService(mockFactory.Object);
var autocompleteService = new AutoCompleteService();
autocompleteService.ConnectionServiceInstance = connectionService;
autocompleteService.InitializeService(Microsoft.SqlTools.ServiceLayer.Hosting.ServiceHost.Instance);
// Open connections
// The cache should get updated as part of this
ConnectParams connectionRequest = TestObjects.GetTestConnectionParams();
connectionRequest.OwnerUri = "file:///my/first/sql/file.sql";
connectionRequest.Connection.DatabaseName = testDb1;
var connectionResult = connectionService.Connect(connectionRequest);
Assert.NotEmpty(connectionResult.ConnectionId);
// Check that there is one cache created in the auto complete service
Assert.Equal(1, autocompleteService.GetCacheCount());
// Open second connection
ConnectParams connectionRequest2 = TestObjects.GetTestConnectionParams();
connectionRequest2.OwnerUri = "file:///my/second/sql/file.sql";
connectionRequest2.Connection.DatabaseName = testDb2;
var connectionResult2 = connectionService.Connect(connectionRequest2);
Assert.NotEmpty(connectionResult2.ConnectionId);
// Check that there are now two caches in the auto complete service
Assert.Equal(2, autocompleteService.GetCacheCount());
// Check that we get 2 different table suggestions for autocomplete requests
TextDocumentPosition position = new TextDocumentPosition();
position.TextDocument = new TextDocumentIdentifier();
position.TextDocument.Uri = connectionRequest.OwnerUri;
position.Position = new Position();
position.Position.Line = 1;
position.Position.Character = 1;
var items = autocompleteService.GetCompletionItems(position);
Assert.Equal(2, items.Length);
Assert.Equal("master", items[0].Label);
Assert.Equal("model", items[1].Label);
TextDocumentPosition position2 = new TextDocumentPosition();
position2.TextDocument = new TextDocumentIdentifier();
position2.TextDocument.Uri = connectionRequest2.OwnerUri;
position2.Position = new Position();
position2.Position.Line = 1;
position2.Position.Character = 1;
var items2 = autocompleteService.GetCompletionItems(position2);
Assert.Equal(3, items2.Length);
Assert.Equal("master", items2[0].Label);
Assert.Equal("my_table", items2[1].Label);
Assert.Equal("my_other_table", items2[2].Label);
}
#endif
#endregion
}
}

View File

@@ -9,14 +9,19 @@ using System.Data;
using System.Data.Common;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SqlServer.Management.SmoMetadataProvider;
using Microsoft.SqlServer.Management.SqlParser.Binder;
using Microsoft.SqlServer.Management.SqlParser.MetadataProvider;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.ServiceLayer.Test.Utility;
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
using Moq;
using Moq.Protected;
@@ -36,6 +41,16 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
public const int StandardColumns = 5;
public static string TestServer { get; set; }
public static string TestDatabase { get; set; }
static Common()
{
TestServer = "sqltools11";
TestDatabase = "master";
}
public static Dictionary<string, string>[] StandardTestData
{
get { return GetTestData(StandardRows, StandardColumns); }
@@ -122,8 +137,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
{
UserName = "sa",
Password = "Yukon900",
DatabaseName = "AdventureWorks2016CTP3_2",
ServerName = "sqltools11"
DatabaseName = Common.TestDatabase,
ServerName = Common.TestServer
};
return new ConnectionInfo(CreateMockFactory(data, throwOnRead), OwnerUri, connDetails);
@@ -133,6 +148,37 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
#region Service Mocking
public static void GetAutoCompleteTestObjects(
out TextDocumentPosition textDocument,
out ScriptFile scriptFile,
out ConnectionInfo connInfo
)
{
textDocument = new TextDocumentPosition();
textDocument.TextDocument = new TextDocumentIdentifier();
textDocument.TextDocument.Uri = Common.OwnerUri;
textDocument.Position = new Position();
textDocument.Position.Line = 0;
textDocument.Position.Character = 0;
connInfo = Common.CreateTestConnectionInfo(null, false);
var srvConn = ConnectionService.GetServerConnection(connInfo);
var displayInfoProvider = new MetadataDisplayInfoProvider();
var metadataProvider = SmoMetadataProvider.CreateConnectedProvider(srvConn);
var binder = BinderProvider.CreateBinder(metadataProvider);
LanguageService.Instance.ScriptParseInfoMap.Add(textDocument.TextDocument.Uri,
new ScriptParseInfo()
{
Binder = binder,
MetadataProvider = metadataProvider,
MetadataDisplayInfoProvider = displayInfoProvider
});
scriptFile = new ScriptFile();
scriptFile.ClientFilePath = textDocument.TextDocument.Uri;
}
public static ConnectionDetails GetTestConnectionDetails()
{
return new ConnectionDetails

View File

@@ -9,7 +9,7 @@
"System.Runtime.Serialization.Primitives": "4.1.1",
"System.Data.Common": "4.1.0",
"System.Data.SqlClient": "4.1.0",
"Microsoft.SqlServer.Smo": "140.1.2",
"Microsoft.SqlServer.Smo": "140.1.5",
"System.Security.SecureString": "4.0.0",
"System.Collections.Specialized": "4.0.1",
"System.ComponentModel.TypeConverter": "4.1.0",