mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-14 09:59:48 -05:00
* Fix for : 4045: Cannot cancel a Query. Query runs too long. HandleExecuteRequest was returning a task to awaiter - which was getting waited on. changed it to async function to start task in new thread and return nothing to awaiter . Also the cancellation token set by cancel request was getting checked only after making the connection to execute batches. Added an additional check for cancellation token befor the connection has been made. Fix for 4319: Error showing dbs when using AAD in... 1.5.0-alpha.74 David has already created a new version of SMO nuget with the fix. - incorporating the same (150.18096.0-preview). * Adding awaitable internal task for tests to run properly * Adding more cancel tests
329 lines
14 KiB
C#
329 lines
14 KiB
C#
//
|
|
// 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.Concurrent;
|
|
using System.Data;
|
|
using System.Data.Common;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.SqlTools.ServiceLayer.Connection;
|
|
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
|
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
|
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
|
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts.ExecuteRequests;
|
|
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
|
using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility;
|
|
using Microsoft.SqlTools.ServiceLayer.Workspace;
|
|
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
|
|
using Microsoft.SqlTools.ServiceLayer.Test.Common;
|
|
using Moq;
|
|
using Moq.Protected;
|
|
using HostingProtocol = Microsoft.SqlTools.Hosting.Protocol;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution
|
|
{
|
|
public static class Common
|
|
{
|
|
#region Constants
|
|
|
|
public const string InvalidQuery = "SELECT *** FROM sys.objects";
|
|
|
|
public const string NoOpQuery = "-- No ops here, just us chickens.";
|
|
|
|
public const int Ordinal = 100; // We'll pick something other than default(int)
|
|
|
|
public const int StandardColumns = 5;
|
|
|
|
public const int StandardRows = 5;
|
|
|
|
public const int ZeroRows = 0;
|
|
|
|
public const int HundredRows = 100;
|
|
|
|
public const int ThousandRows = 1000;
|
|
|
|
public const int HundredKRows = 100000;
|
|
|
|
public const int MillionRows = 1000000;
|
|
|
|
public const int TenMillionRows = 10000000;
|
|
|
|
public const int HundredMillionRows = 100000000;
|
|
|
|
public const SelectionData WholeDocument = null;
|
|
|
|
public static readonly ConnectionDetails StandardConnectionDetails = new ConnectionDetails
|
|
{
|
|
DatabaseName = "123",
|
|
Password = "456",
|
|
ServerName = "789",
|
|
UserName = "012"
|
|
};
|
|
|
|
public static readonly SelectionData SubsectionDocument = new SelectionData(0, 0, 2, 2);
|
|
|
|
#endregion
|
|
public static TestResultSet ZeroRowTestResultSet => new TestResultSet(StandardColumns, ZeroRows);
|
|
|
|
public static TestResultSet[] ZeroRowTestDataSet => new[] { ZeroRowTestResultSet };
|
|
|
|
public static TestResultSet StandardTestResultSet => new TestResultSet(StandardColumns, StandardRows);
|
|
|
|
public static TestResultSet[] StandardTestDataSet => new[] { StandardTestResultSet };
|
|
|
|
|
|
public static TestResultSet HundredRowTestResultSet => new TestResultSet(StandardColumns, HundredRows);
|
|
|
|
public static TestResultSet[] HundredRowTestDataSet => new[] { HundredRowTestResultSet };
|
|
|
|
|
|
public static TestResultSet ThousandRowTestResultSet => new TestResultSet(StandardColumns, ThousandRows);
|
|
|
|
public static TestResultSet[] ThousandRowTestDataSet => new[] { ThousandRowTestResultSet };
|
|
|
|
private static readonly Lazy<TestResultSet> HundredKRowTestResultSetLazy = new Lazy<TestResultSet>(new TestResultSet(StandardColumns, HundredKRows));
|
|
|
|
public static TestResultSet HundredKRowTestResultSet => HundredKRowTestResultSetLazy.Value;
|
|
|
|
private static readonly Lazy<TestResultSet[]> HundredKRowTestDataSetLazy = new Lazy<TestResultSet[]>(new[] { HundredKRowTestResultSet });
|
|
|
|
public static TestResultSet[] HundredKRowTestDataSet => HundredKRowTestDataSetLazy.Value;
|
|
|
|
|
|
private static readonly Lazy<TestResultSet> MillionRowTestResultSetLazy = new Lazy<TestResultSet>(new TestResultSet(StandardColumns, MillionRows));
|
|
|
|
public static TestResultSet MillionRowTestResultSet => MillionRowTestResultSetLazy.Value;
|
|
|
|
private static readonly Lazy<TestResultSet[]> MillionRowTestDataSetLazy = new Lazy<TestResultSet[]>(new[]{ MillionRowTestResultSet });
|
|
|
|
public static TestResultSet[] MillionRowTestDataSet => MillionRowTestDataSetLazy.Value;
|
|
|
|
public static IEnumerable<TestResultSet> TestResultSetsEnumeration
|
|
{
|
|
get
|
|
{
|
|
yield return StandardTestResultSet;
|
|
yield return HundredRowTestResultSet;
|
|
yield return ThousandRowTestResultSet;
|
|
yield return ZeroRowTestResultSet;
|
|
yield return HundredKRowTestResultSet;
|
|
yield return MillionRowTestResultSet;
|
|
}
|
|
}
|
|
|
|
public static TestResultSet[] ExecutionPlanTestDataSet
|
|
{
|
|
get
|
|
{
|
|
DbColumn[] columns = { new TestDbColumn("Microsoft SQL Server 2005 XML Showplan") };
|
|
object[][] rows = { new object[] { "Execution Plan" } };
|
|
return new[] {new TestResultSet(columns, rows)};
|
|
}
|
|
}
|
|
|
|
#region Public Methods
|
|
|
|
public static Batch GetBasicExecutedBatch()
|
|
{
|
|
Batch batch = new Batch(Constants.StandardQuery, SubsectionDocument, 1,
|
|
MemoryFileSystem.GetFileStreamFactory());
|
|
batch.Execute(CreateTestConnection(StandardTestDataSet, false, false), CancellationToken.None).Wait();
|
|
return batch;
|
|
}
|
|
|
|
public static Batch GetExecutedBatchWithExecutionPlan()
|
|
{
|
|
Batch batch = new Batch(Constants.StandardQuery, SubsectionDocument, 1,
|
|
MemoryFileSystem.GetFileStreamFactory());
|
|
batch.Execute(CreateTestConnection(ExecutionPlanTestDataSet, false, false), CancellationToken.None).Wait();
|
|
return batch;
|
|
}
|
|
|
|
public static Query GetBasicExecutedQuery()
|
|
{
|
|
ConnectionInfo ci = CreateConnectedConnectionInfo(StandardTestDataSet, false, false);
|
|
|
|
// Query won't be able to request a new query DbConnection unless the ConnectionService has a
|
|
// ConnectionInfo with the same URI as the query, so we will manually set it
|
|
ConnectionService.Instance.OwnerToConnectionMap[ci.OwnerUri] = ci;
|
|
|
|
Query query = new Query(Constants.StandardQuery, ci, new QueryExecutionSettings(),
|
|
MemoryFileSystem.GetFileStreamFactory());
|
|
query.Execute();
|
|
query.ExecutionTask.Wait();
|
|
return query;
|
|
}
|
|
|
|
public static Query GetBasicExecutedQuery(QueryExecutionSettings querySettings)
|
|
{
|
|
ConnectionInfo ci = CreateConnectedConnectionInfo(StandardTestDataSet, false, false);
|
|
|
|
// Query won't be able to request a new query DbConnection unless the ConnectionService has a
|
|
// ConnectionInfo with the same URI as the query, so we will manually set it
|
|
ConnectionService.Instance.OwnerToConnectionMap[ci.OwnerUri] = ci;
|
|
|
|
Query query = new Query(Constants.StandardQuery, ci, querySettings,
|
|
MemoryFileSystem.GetFileStreamFactory());
|
|
query.Execute();
|
|
query.ExecutionTask.Wait();
|
|
return query;
|
|
}
|
|
|
|
public static TestResultSet[] GetTestDataSet(int dataSets)
|
|
{
|
|
return Enumerable.Repeat(StandardTestResultSet, dataSets).ToArray();
|
|
}
|
|
|
|
public static async Task AwaitExecution(QueryExecutionService service, ExecuteDocumentSelectionParams qeParams,
|
|
HostingProtocol.RequestContext<ExecuteRequestResult> requestContext)
|
|
{
|
|
await service.HandleExecuteRequest(qeParams, requestContext);
|
|
await service.WorkTask;
|
|
if (service.ActiveQueries.ContainsKey(qeParams.OwnerUri) && service.ActiveQueries[qeParams.OwnerUri].ExecutionTask != null)
|
|
{
|
|
await service.ActiveQueries[qeParams.OwnerUri].ExecutionTask;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region DbConnection Mocking
|
|
|
|
private static DbCommand CreateTestCommand(TestResultSet[] data, bool throwOnExecute, bool throwOnRead)
|
|
{
|
|
var commandMock = new Mock<DbCommand> { CallBase = true };
|
|
var commandMockSetup = commandMock.Protected()
|
|
.Setup<DbDataReader>("ExecuteDbDataReader", It.IsAny<CommandBehavior>());
|
|
|
|
// Setup the expected execute behavior
|
|
if (throwOnExecute)
|
|
{
|
|
var mockException = new Mock<DbException>();
|
|
mockException.SetupGet(dbe => dbe.Message).Returns("Message");
|
|
commandMockSetup.Throws(mockException.Object);
|
|
}
|
|
else
|
|
{
|
|
commandMockSetup.Returns(new TestDbDataReader(data, throwOnRead));
|
|
}
|
|
|
|
|
|
return commandMock.Object;
|
|
}
|
|
|
|
private static DbConnection CreateTestConnection(TestResultSet[] data, bool throwOnExecute, bool throwOnRead)
|
|
{
|
|
var connectionMock = new Mock<DbConnection> { CallBase = true };
|
|
connectionMock.Protected()
|
|
.Setup<DbCommand>("CreateDbCommand")
|
|
.Returns(() => CreateTestCommand(data, throwOnExecute, throwOnRead));
|
|
connectionMock.Setup(dbc => dbc.Open())
|
|
.Callback(() => connectionMock.SetupGet(dbc => dbc.State).Returns(ConnectionState.Open));
|
|
connectionMock.Setup(dbc => dbc.Close())
|
|
.Callback(() => connectionMock.SetupGet(dbc => dbc.State).Returns(ConnectionState.Closed));
|
|
|
|
return connectionMock.Object;
|
|
}
|
|
|
|
private static ISqlConnectionFactory CreateMockFactory(TestResultSet[] data, bool throwOnExecute, bool throwOnRead)
|
|
{
|
|
var mockFactory = new Mock<ISqlConnectionFactory>();
|
|
mockFactory.Setup(factory => factory.CreateSqlConnection(It.IsAny<string>(), It.IsAny<string>()))
|
|
.Returns(() => CreateTestConnection(data, throwOnExecute, throwOnRead));
|
|
|
|
return mockFactory.Object;
|
|
}
|
|
|
|
public static ConnectionInfo CreateTestConnectionInfo(TestResultSet[] data, bool throwOnExecute, bool throwOnRead)
|
|
{
|
|
// Create a connection info and add the default connection to it
|
|
ISqlConnectionFactory factory = CreateMockFactory(data, throwOnExecute, throwOnRead);
|
|
ConnectionInfo ci = new ConnectionInfo(factory, Constants.OwnerUri, StandardConnectionDetails);
|
|
ci.ConnectionTypeToConnectionMap[ConnectionType.Default] = factory.CreateSqlConnection(null, null);
|
|
return ci;
|
|
}
|
|
|
|
public static ConnectionInfo CreateConnectedConnectionInfo(
|
|
TestResultSet[] data,
|
|
bool throwOnExecute,
|
|
bool throwOnRead,
|
|
string type = ConnectionType.Default)
|
|
{
|
|
ConnectionService connectionService = ConnectionService.Instance;
|
|
connectionService.OwnerToConnectionMap.Clear();
|
|
connectionService.ConnectionFactory = CreateMockFactory(data, throwOnExecute, throwOnRead);
|
|
|
|
ConnectParams connectParams = new ConnectParams
|
|
{
|
|
Connection = StandardConnectionDetails,
|
|
OwnerUri = Constants.OwnerUri,
|
|
Type = type
|
|
};
|
|
|
|
connectionService.Connect(connectParams).Wait();
|
|
return connectionService.OwnerToConnectionMap[connectParams.OwnerUri];
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Service Mocking
|
|
|
|
public static QueryExecutionService GetPrimedExecutionService(
|
|
TestResultSet[] data,
|
|
bool isConnected,
|
|
bool throwOnExecute,
|
|
bool throwOnRead,
|
|
WorkspaceService<SqlToolsSettings> workspaceService,
|
|
out ConcurrentDictionary<string, byte[]> storage,
|
|
int sizeFactor=1)
|
|
{
|
|
// Create a place for the temp "files" to be written
|
|
storage = new ConcurrentDictionary<string, byte[]>();
|
|
|
|
// Mock the connection service
|
|
var connectionService = new Mock<ConnectionService>();
|
|
ConnectionInfo ci = CreateConnectedConnectionInfo(data, throwOnExecute, throwOnRead);
|
|
ConnectionInfo outValMock;
|
|
connectionService
|
|
.Setup(service => service.TryFindConnection(It.IsAny<string>(), out outValMock))
|
|
.OutCallback((string owner, out ConnectionInfo connInfo) => connInfo = isConnected ? ci : null)
|
|
.Returns(isConnected);
|
|
|
|
return new QueryExecutionService(connectionService.Object, workspaceService) { BufferFileStreamFactory = MemoryFileSystem.GetFileStreamFactory(storage, sizeFactor) };
|
|
}
|
|
|
|
public static QueryExecutionService GetPrimedExecutionService(
|
|
TestResultSet[] data,
|
|
bool isConnected,
|
|
bool throwOnExecute,
|
|
bool throwOnRead,
|
|
WorkspaceService<SqlToolsSettings> workspaceService,
|
|
int sizeFactor=1)
|
|
{
|
|
ConcurrentDictionary<string, byte[]> storage;
|
|
return GetPrimedExecutionService(data, isConnected, throwOnExecute, throwOnRead, workspaceService, out storage, sizeFactor);
|
|
}
|
|
|
|
public static WorkspaceService<SqlToolsSettings> GetPrimedWorkspaceService(string query)
|
|
{
|
|
// Set up file for returning the query
|
|
var fileMock = new Mock<ScriptFile>();
|
|
fileMock.SetupGet(file => file.Contents).Returns(query);
|
|
|
|
// Set up workspace mock
|
|
var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>();
|
|
workspaceService.Setup(service => service.Workspace.GetFile(It.IsAny<string>()))
|
|
.Returns(fileMock.Object);
|
|
|
|
return workspaceService.Object;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|