mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-16 01:25:41 -05:00
Feature execution plan settings and request implementation (#213)
* experimental showplan implementation (tools side only) * fix for redundant massages from showplan executions * moved showplan batches to new variables to make it less confusing * returns showplan as part of batch summary with in each result summary * cleaned up showplan resultsets * cleaning up code and making showplan var optional * changes all var names to showplan * adding estimated support * small fixes * updatin var names and adding EPOptions struct * adding ssms execution plan logic based on server types * adding special actions logic * removing redundant name changes * execution plan query handler added * cleaning up functions and data structures * seperated special actions into its own class * cleaning up special actions * cleaning up code * small new line fixes * commenting out pre-yukon code * removing all pre yukon code * last yukon code commented out * fixes broken tests * adding related unit tests; integration tests incoming * finishing tests and cleaning up code * semantic changes * cleaning up semantics * changes and test fixes, also adding new exceptions into RS * fixing special actions and cleaning up request logic * fixing comment to trigger new build * triggering another build * fixed up specialaction and added tests for it
This commit is contained in:
@@ -72,6 +72,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
return batch;
|
||||
}
|
||||
|
||||
public static Batch GetExecutedBatchWithExecutionPlan()
|
||||
{
|
||||
Batch batch = new Batch(StandardQuery, SubsectionDocument, 1, GetFileStreamFactory(new Dictionary<string, byte[]>()));
|
||||
batch.Execute(CreateTestConnection(new[] {GetExecutionPlanTestData()}, false), CancellationToken.None).Wait();
|
||||
return batch;
|
||||
}
|
||||
|
||||
public static Query GetBasicExecutedQuery()
|
||||
{
|
||||
ConnectionInfo ci = CreateTestConnectionInfo(new[] {StandardTestData}, false);
|
||||
@@ -81,6 +88,15 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
return query;
|
||||
}
|
||||
|
||||
public static Query GetBasicExecutedQuery(QueryExecutionSettings querySettings)
|
||||
{
|
||||
ConnectionInfo ci = CreateTestConnectionInfo(new[] {StandardTestData}, false);
|
||||
Query query = new Query(StandardQuery, ci, querySettings, GetFileStreamFactory(new Dictionary<string, byte[]>()));
|
||||
query.Execute();
|
||||
query.ExecutionTask.Wait();
|
||||
return query;
|
||||
}
|
||||
|
||||
public static Dictionary<string, string>[] GetTestData(int columns, int rows)
|
||||
{
|
||||
Dictionary<string, string>[] output = new Dictionary<string, string>[rows];
|
||||
@@ -97,6 +113,19 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
public static Dictionary<string, string>[] GetExecutionPlanTestData()
|
||||
{
|
||||
Dictionary<string, string>[] output = new Dictionary<string, string>[1];
|
||||
int col = 0;
|
||||
int row = 0;
|
||||
Dictionary<string, string> rowDictionary = new Dictionary<string, string>();
|
||||
rowDictionary.Add(string.Format("Microsoft SQL Server 2005 XML Showplan", col), string.Format("Execution Plan", col, row));
|
||||
output[row] = rowDictionary;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public static Dictionary<string, string>[][] GetTestDataSet(int dataSets)
|
||||
{
|
||||
List<Dictionary<string, string>[]> output = new List<Dictionary<string, string>[]>();
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
//
|
||||
// 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.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Test.Utility;
|
||||
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
{
|
||||
public class ExecutionPlanTests
|
||||
{
|
||||
#region ResultSet Class Tests
|
||||
|
||||
[Fact]
|
||||
public void ExecutionPlanValid()
|
||||
{
|
||||
// Setup:
|
||||
// ... I have a batch that has been executed with a execution plan
|
||||
Batch b = Common.GetExecutedBatchWithExecutionPlan();
|
||||
|
||||
// If:
|
||||
// ... I have a result set and I ask for a valid execution plan
|
||||
ResultSet planResultSet = b.ResultSets.First();
|
||||
ExecutionPlan plan = planResultSet.GetExecutionPlan().Result;
|
||||
|
||||
// Then:
|
||||
// ... I should get the execution plan back
|
||||
Assert.Equal("xml", plan.Format);
|
||||
Assert.Contains("Execution Plan", plan.Content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExecutionPlanInvalid()
|
||||
{
|
||||
// Setup:
|
||||
// ... I have a batch that has been executed
|
||||
Batch b = Common.GetBasicExecutedBatch();
|
||||
|
||||
// If:
|
||||
// ... I have a result set and I ask for an execution plan that doesn't exist
|
||||
ResultSet planResultSet = b.ResultSets.First();
|
||||
|
||||
// Then:
|
||||
// ... It should throw an exception
|
||||
Assert.ThrowsAsync<Exception>(async () => await planResultSet.GetExecutionPlan());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Batch Class Tests
|
||||
|
||||
[Fact]
|
||||
public void BatchExecutionPlanValidTest()
|
||||
{
|
||||
// If I have an executed batch which has an execution plan
|
||||
Batch b = Common.GetExecutedBatchWithExecutionPlan();
|
||||
|
||||
// ... And I ask for a valid execution plan
|
||||
ExecutionPlan plan = b.GetExecutionPlan(0).Result;
|
||||
|
||||
// Then:
|
||||
// ... I should get the execution plan back
|
||||
Assert.Equal("xml", plan.Format);
|
||||
Assert.Contains("Execution Plan", plan.Content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BatchExecutionPlanInvalidTest()
|
||||
{
|
||||
// Setup:
|
||||
// ... I have a batch that has been executed without an execution plan
|
||||
Batch b = Common.GetBasicExecutedBatch();
|
||||
|
||||
// If:
|
||||
// ... I ask for an invalid execution plan
|
||||
Assert.ThrowsAsync<Exception>(async () => await b.GetExecutionPlan(0));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(-1)] // Invalid result set, too low
|
||||
[InlineData(2)] // Invalid result set, too high
|
||||
public void BatchExecutionPlanInvalidParamsTest(int resultSetIndex)
|
||||
{
|
||||
// If I have an executed batch which has an execution plan
|
||||
Batch b = Common.GetExecutedBatchWithExecutionPlan();
|
||||
|
||||
// ... And I ask for an execution plan with an invalid result set index
|
||||
// Then:
|
||||
// ... It should throw an exception
|
||||
Assert.ThrowsAsync<ArgumentOutOfRangeException>(async () => await b.GetExecutionPlan(resultSetIndex));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Query Class Tests
|
||||
|
||||
[Theory]
|
||||
[InlineData(-1)] // Invalid batch, too low
|
||||
[InlineData(2)] // Invalid batch, too high
|
||||
public void QueryExecutionPlanInvalidParamsTest(int batchIndex)
|
||||
{
|
||||
// Setup query settings
|
||||
QueryExecutionSettings querySettings = new QueryExecutionSettings();
|
||||
querySettings.ExecutionPlanOptions = new ExecutionPlanOptions()
|
||||
{
|
||||
IncludeActualExecutionPlanXml = false,
|
||||
IncludeEstimatedExecutionPlanXml = true
|
||||
};
|
||||
|
||||
// If I have an executed query
|
||||
Query q = Common.GetBasicExecutedQuery(querySettings);
|
||||
|
||||
// ... And I ask for a subset with an invalid result set index
|
||||
// Then:
|
||||
// ... It should throw an exception
|
||||
Assert.ThrowsAsync<ArgumentOutOfRangeException>(async () => await q.GetExecutionPlan(batchIndex, 0));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Service Intergration Tests
|
||||
|
||||
[Fact]
|
||||
public async Task ExecutionPlanServiceValidTest()
|
||||
{
|
||||
// If:
|
||||
// ... I have a query that has results in the form of an execution plan
|
||||
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
|
||||
var queryService = Common.GetPrimedExecutionService(new[] {Common.GetExecutionPlanTestData()}, true, false, workspaceService);
|
||||
var executeParams = new QueryExecuteParams {QuerySelection = null, OwnerUri = Common.OwnerUri};
|
||||
executeParams.ExecutionPlanOptions = new ExecutionPlanOptions()
|
||||
{
|
||||
IncludeActualExecutionPlanXml = false,
|
||||
IncludeEstimatedExecutionPlanXml = true
|
||||
};
|
||||
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
|
||||
await queryService.HandleExecuteRequest(executeParams, executeRequest.Object);
|
||||
await queryService.ActiveQueries[Common.OwnerUri].ExecutionTask;
|
||||
|
||||
// ... And I then ask for a valid execution plan
|
||||
var executionPlanParams = new QueryExecutionPlanParams { OwnerUri = Common.OwnerUri, BatchIndex = 0, ResultSetIndex = 0 };
|
||||
var executionPlanRequest = new EventFlowValidator<QueryExecutionPlanResult>()
|
||||
.AddResultValidation(r =>
|
||||
{
|
||||
// Then: Messages should be null and execution plan should not be null
|
||||
Assert.NotNull(r.ExecutionPlan);
|
||||
}).Complete();
|
||||
await queryService.HandleExecutionPlanRequest(executionPlanParams, executionPlanRequest.Object);
|
||||
executionPlanRequest.Validate();
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task ExecutionPlanServiceMissingQueryTest()
|
||||
{
|
||||
// If:
|
||||
// ... I ask for an execution plan for a file that hasn't executed a query
|
||||
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
|
||||
var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
|
||||
var executionPlanParams = new QueryExecutionPlanParams { OwnerUri = Common.OwnerUri, ResultSetIndex = 0, BatchIndex = 0 };
|
||||
var executionPlanRequest = new EventFlowValidator<QueryExecutionPlanResult>()
|
||||
.AddErrorValidation<string>(r =>
|
||||
{
|
||||
// Then: It should return a populated error
|
||||
Assert.NotNull(r);
|
||||
}).Complete();
|
||||
await queryService.HandleExecutionPlanRequest(executionPlanParams, executionPlanRequest.Object);
|
||||
executionPlanRequest.Validate();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExecutionPlanServiceUnexecutedQueryTest()
|
||||
{
|
||||
// If:
|
||||
// ... I have a query that hasn't finished executing (doesn't matter what)
|
||||
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
|
||||
var queryService = Common.GetPrimedExecutionService(new[] { Common.GetExecutionPlanTestData() }, true, false, workspaceService);
|
||||
var executeParams = new QueryExecuteParams { QuerySelection = null, OwnerUri = Common.OwnerUri };
|
||||
executeParams.ExecutionPlanOptions = new ExecutionPlanOptions()
|
||||
{
|
||||
IncludeActualExecutionPlanXml = false,
|
||||
IncludeEstimatedExecutionPlanXml = true
|
||||
};
|
||||
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
|
||||
await queryService.HandleExecuteRequest(executeParams, executeRequest.Object);
|
||||
await queryService.ActiveQueries[Common.OwnerUri].ExecutionTask;
|
||||
queryService.ActiveQueries[Common.OwnerUri].Batches[0].ResultSets[0].hasBeenRead = false;
|
||||
|
||||
// ... And I then ask for a valid execution plan from it
|
||||
var executionPlanParams = new QueryExecutionPlanParams { OwnerUri = Common.OwnerUri, ResultSetIndex = 0, BatchIndex = 0 };
|
||||
var executionPlanRequest = new EventFlowValidator<QueryExecutionPlanResult>()
|
||||
.AddErrorValidation<string>(r =>
|
||||
{
|
||||
// Then: It should return a populated error
|
||||
Assert.NotNull(r);
|
||||
}).Complete();
|
||||
await queryService.HandleExecutionPlanRequest(executionPlanParams, executionPlanRequest.Object);
|
||||
executionPlanRequest.Validate();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExecutionPlanServiceOutOfRangeSubsetTest()
|
||||
{
|
||||
// If:
|
||||
// ... I have a query that doesn't have any result sets
|
||||
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
|
||||
var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
|
||||
var executeParams = new QueryExecuteParams { QuerySelection = null, OwnerUri = Common.OwnerUri };
|
||||
executeParams.ExecutionPlanOptions = new ExecutionPlanOptions()
|
||||
{
|
||||
IncludeActualExecutionPlanXml = false,
|
||||
IncludeEstimatedExecutionPlanXml = true
|
||||
};
|
||||
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
|
||||
await queryService.HandleExecuteRequest(executeParams, executeRequest.Object);
|
||||
await queryService.ActiveQueries[Common.OwnerUri].ExecutionTask;
|
||||
|
||||
// ... And I then ask for an execution plan from a result set
|
||||
var executionPlanParams = new QueryExecutionPlanParams { OwnerUri = Common.OwnerUri, ResultSetIndex = 0, BatchIndex = 0 };
|
||||
var executionPlanRequest = new EventFlowValidator<QueryExecutionPlanResult>()
|
||||
.AddErrorValidation<string>(r =>
|
||||
{
|
||||
// Then: It should return a populated error
|
||||
Assert.NotNull(r);
|
||||
}).Complete();
|
||||
await queryService.HandleExecutionPlanRequest(executionPlanParams, executionPlanRequest.Object);
|
||||
executionPlanRequest.Validate();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
||||
using Microsoft.SqlTools.ServiceLayer.Test.Utility;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
{
|
||||
public class SpecialActionTests
|
||||
{
|
||||
|
||||
[Fact]
|
||||
public void SpecialActionInstantiation()
|
||||
{
|
||||
// If:
|
||||
// ... I create a special action object
|
||||
var specialAction = new SpecialAction();
|
||||
|
||||
// Then:
|
||||
// ... The special action should be set to none and only none
|
||||
Assert.Equal(true, specialAction.None);
|
||||
Assert.Equal(false, specialAction.ExpectYukonXMLShowPlan);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpecialActionNoneProperty()
|
||||
{
|
||||
// If:
|
||||
// ... I create a special action object and add properties but set it back to none
|
||||
var specialAction = new SpecialAction();
|
||||
specialAction.ExpectYukonXMLShowPlan = true;
|
||||
specialAction.None = true;
|
||||
|
||||
// Then:
|
||||
// ... The special action should be set to none and only none
|
||||
Assert.Equal(true, specialAction.None);
|
||||
Assert.Equal(false, specialAction.ExpectYukonXMLShowPlan);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpecialActionExpectYukonXmlShowPlanReset()
|
||||
{
|
||||
// If:
|
||||
// ... I create a special action object and add properties but set the property back to false
|
||||
var specialAction = new SpecialAction();
|
||||
specialAction.ExpectYukonXMLShowPlan = true;
|
||||
specialAction.ExpectYukonXMLShowPlan = false;
|
||||
|
||||
// Then:
|
||||
// ... The special action should be set to none and only none
|
||||
Assert.Equal(true, specialAction.None);
|
||||
Assert.Equal(false, specialAction.ExpectYukonXMLShowPlan);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpecialActionCombiningProperties()
|
||||
{
|
||||
// If:
|
||||
// ... I create a special action object and add properties and combine with the same property
|
||||
var specialAction = new SpecialAction();
|
||||
specialAction.ExpectYukonXMLShowPlan = true;
|
||||
|
||||
var specialAction2 = new SpecialAction();
|
||||
specialAction2.ExpectYukonXMLShowPlan = true;
|
||||
|
||||
specialAction.CombineSpecialAction(specialAction2);
|
||||
|
||||
// Then:
|
||||
// ... The special action should be set to none and only none
|
||||
Assert.Equal(false, specialAction.None);
|
||||
Assert.Equal(true, specialAction.ExpectYukonXMLShowPlan);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user