mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-09 01:32:38 -05:00
Adds new graph comparison request handler to the Execution Plan Service. (#1438)
* Adds new graph comparison request handler to the Execution Plan Service. * Code review changes * Renames execution plan compare params, result, and request classes.
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// An ExecutionGraphComparisonResult is composed of an execution plan node, but has additional properties
|
||||
/// to keep track of matching ExecutionGraphComparisonResult nodes for execution plan nodes present in the
|
||||
/// the graph being compared against. This class also features a group index that can assist
|
||||
/// with coloring similar sections of execution plans in the UI.
|
||||
/// </summary>
|
||||
public class ExecutionGraphComparisonResult
|
||||
{
|
||||
/// <summary>
|
||||
/// The base ExecutionPlanNode for the ExecutionGraphComparisonResult.
|
||||
/// </summary>
|
||||
public ExecutionPlanNode BaseNode { get; set; }
|
||||
/// <summary>
|
||||
/// The children of the ExecutionGraphComparisonResult.
|
||||
/// </summary>
|
||||
public List<ExecutionGraphComparisonResult> Children { get; set; } = new List<ExecutionGraphComparisonResult>();
|
||||
/// <summary>
|
||||
/// The group index of the ExecutionGraphComparisonResult.
|
||||
/// </summary>
|
||||
public int GroupIndex { get; set; }
|
||||
/// <summary>
|
||||
/// Flag to indicate if the ExecutionGraphComparisonResult has a matching node in the compared execution plan.
|
||||
/// </summary>
|
||||
public bool HasMatch { get; set; }
|
||||
/// <summary>
|
||||
/// List of matching nodes for the ExecutionGraphComparisonResult.
|
||||
/// </summary>
|
||||
public List<ExecutionGraphComparisonResult> MatchingNodes { get; set; } = new List<ExecutionGraphComparisonResult>();
|
||||
/// <summary>
|
||||
/// The parent of the ExecutionGraphComparisonResult.
|
||||
/// </summary>
|
||||
public ExecutionGraphComparisonResult ParentNode { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// 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.Hosting.Protocol.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan.Contracts
|
||||
{
|
||||
public class ExecutionPlanComparisonParams
|
||||
{
|
||||
/// <summary>
|
||||
/// First query execution plan for comparison.
|
||||
/// </summary>
|
||||
public ExecutionPlanGraphInfo FirstExecutionPlanGraphInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Second query execution plan for comparison.
|
||||
/// </summary>
|
||||
public ExecutionPlanGraphInfo SecondExecutionPlanGraphInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Flag to indicate if the database name should be ignored
|
||||
/// during comparisons.
|
||||
/// </summary>
|
||||
public bool IgnoreDatabaseName { get; set; }
|
||||
}
|
||||
|
||||
public class ExecutionPlanComparisonResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Created ExecutionGraphComparisonResult for the first execution plan
|
||||
/// </summary>
|
||||
public ExecutionGraphComparisonResult FirstComparisonResult { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Created ExecutionGraphComparisonResult for the second execution plan
|
||||
/// </summary>
|
||||
public ExecutionGraphComparisonResult SecondComparisonResult { get; set; }
|
||||
}
|
||||
|
||||
public class ExecutionPlanComparisonRequest
|
||||
{
|
||||
public static readonly
|
||||
RequestType<ExecutionPlanComparisonParams, ExecutionPlanComparisonResult> Type =
|
||||
RequestType<ExecutionPlanComparisonParams, ExecutionPlanComparisonResult>.Create("executionPlan/compareExecutionPlanGraph");
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Execution plan graph object that is sent over JSON RPC
|
||||
@@ -32,6 +32,10 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
||||
|
||||
public class ExecutionPlanNode
|
||||
{
|
||||
/// <summary>
|
||||
/// ID for the node.
|
||||
/// </summary>
|
||||
public int ID { get; set; }
|
||||
/// <summary>
|
||||
/// Type of the node. This determines the icon that is displayed for it
|
||||
/// </summary>
|
||||
@@ -146,7 +150,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
||||
public string QueryWithDescription { get; set; }
|
||||
}
|
||||
|
||||
public class ExecutionPlanGraphInfo
|
||||
public class ExecutionPlanGraphInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// File contents
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
||||
{
|
||||
|
||||
@@ -10,14 +10,15 @@ using System.Linq;
|
||||
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
||||
{
|
||||
public class ShowPlanGraphUtils
|
||||
public class ExecutionPlanGraphUtils
|
||||
{
|
||||
public static List<ExecutionPlanGraph> CreateShowPlanGraph(string xml, string fileName)
|
||||
{
|
||||
ShowPlan.ShowPlanGraph[] graphs = ShowPlan.ShowPlanGraph.ParseShowPlanXML(xml, ShowPlan.ShowPlanType.Unknown);
|
||||
ShowPlanGraph[] graphs = ShowPlanGraph.ParseShowPlanXML(xml, ShowPlan.ShowPlanType.Unknown);
|
||||
return graphs.Select(g => new ExecutionPlanGraph
|
||||
{
|
||||
Root = ConvertShowPlanTreeToExecutionPlanTree(g.Root),
|
||||
@@ -31,10 +32,11 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
private static ExecutionPlanNode ConvertShowPlanTreeToExecutionPlanTree(Node currentNode)
|
||||
public static ExecutionPlanNode ConvertShowPlanTreeToExecutionPlanTree(Node currentNode)
|
||||
{
|
||||
return new ExecutionPlanNode
|
||||
{
|
||||
ID = currentNode.ID,
|
||||
Type = currentNode.Operation.Image,
|
||||
Cost = currentNode.Cost,
|
||||
SubTreeCost = currentNode.SubtreeCost,
|
||||
@@ -49,7 +51,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
||||
};
|
||||
}
|
||||
|
||||
private static ExecutionPlanEdges ConvertShowPlanEdgeToExecutionPlanEdge(Edge edge)
|
||||
public static ExecutionPlanEdges ConvertShowPlanEdgeToExecutionPlanEdge(Edge edge)
|
||||
{
|
||||
return new ExecutionPlanEdges
|
||||
{
|
||||
@@ -59,7 +61,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
||||
};
|
||||
}
|
||||
|
||||
private static List<ExecutionPlanGraphPropertyBase> GetProperties(PropertyDescriptorCollection props)
|
||||
public static List<ExecutionPlanGraphPropertyBase> GetProperties(PropertyDescriptorCollection props)
|
||||
{
|
||||
List<ExecutionPlanGraphPropertyBase> propsList = new List<ExecutionPlanGraphPropertyBase>();
|
||||
foreach (PropertyValue prop in props)
|
||||
@@ -96,7 +98,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
||||
return propsList;
|
||||
}
|
||||
|
||||
private static List<ExecutionPlanRecommendation> ParseRecommendations(ShowPlan.ShowPlanGraph g, string fileName)
|
||||
private static List<ExecutionPlanRecommendation> ParseRecommendations(ShowPlanGraph g, string fileName)
|
||||
{
|
||||
return g.Description.MissingIndices.Select(mi => new ExecutionPlanRecommendation
|
||||
{
|
||||
@@ -148,5 +150,58 @@ GO
|
||||
return String.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public static void CopyMatchingNodesIntoSkeletonDTO(ExecutionGraphComparisonResult destRoot, ExecutionGraphComparisonResult srcRoot)
|
||||
{
|
||||
var srcGraphLookupTable = srcRoot.CreateSkeletonLookupTable();
|
||||
|
||||
var queue = new Queue<ExecutionGraphComparisonResult>();
|
||||
queue.Enqueue(destRoot);
|
||||
|
||||
while (queue.Count != 0)
|
||||
{
|
||||
var curNode = queue.Dequeue();
|
||||
|
||||
for (int index = 0; index < curNode.MatchingNodes.Count; ++index)
|
||||
{
|
||||
var matchingId = curNode.MatchingNodes[index].BaseNode.ID;
|
||||
var matchingNode = srcGraphLookupTable[matchingId];
|
||||
|
||||
curNode.MatchingNodes[index] = matchingNode;
|
||||
}
|
||||
|
||||
foreach (var child in curNode.Children)
|
||||
{
|
||||
queue.Enqueue(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExecutionGraphComparisonResultExtensions
|
||||
{
|
||||
public static Dictionary<int, ExecutionGraphComparisonResult> CreateSkeletonLookupTable(this ExecutionGraphComparisonResult node)
|
||||
{
|
||||
var skeletonNodeTable = new Dictionary<int, ExecutionGraphComparisonResult>();
|
||||
var queue = new Queue<ExecutionGraphComparisonResult>();
|
||||
queue.Enqueue(node);
|
||||
|
||||
while (queue.Count != 0)
|
||||
{
|
||||
var curNode = queue.Dequeue();
|
||||
|
||||
if (!skeletonNodeTable.ContainsKey(curNode.BaseNode.ID))
|
||||
{
|
||||
skeletonNodeTable[curNode.BaseNode.ID] = curNode;
|
||||
}
|
||||
|
||||
foreach (var child in curNode.Children)
|
||||
{
|
||||
queue.Enqueue(child);
|
||||
}
|
||||
}
|
||||
|
||||
return skeletonNodeTable;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,12 +6,15 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan;
|
||||
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan.Comparison;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
||||
{
|
||||
/// <summary>
|
||||
/// Main class for Migration Service functionality
|
||||
/// Main class for Execution Plan Service functionality
|
||||
/// </summary>
|
||||
public sealed class ExecutionPlanService : IDisposable
|
||||
{
|
||||
@@ -20,9 +23,9 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
||||
private bool disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new MigrationService instance with default parameters
|
||||
/// Construct a new Execution Plan Service instance with default parameters
|
||||
/// </summary>
|
||||
public ExecutionPlanService()
|
||||
private ExecutionPlanService()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -38,26 +41,23 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
||||
/// Service host object for sending/receiving requests/events.
|
||||
/// Internal for testing purposes.
|
||||
/// </summary>
|
||||
internal IProtocolEndpoint ServiceHost
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
internal IProtocolEndpoint ServiceHost { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the ShowPlan Service instance
|
||||
/// Initializes the Execution Plan Service instance
|
||||
/// </summary>
|
||||
public void InitializeService(ServiceHost serviceHost)
|
||||
{
|
||||
this.ServiceHost = serviceHost;
|
||||
this.ServiceHost.SetRequestHandler(GetExecutionPlanRequest.Type, HandleGetExecutionPlan);
|
||||
ServiceHost = serviceHost;
|
||||
ServiceHost.SetRequestHandler(GetExecutionPlanRequest.Type, HandleGetExecutionPlan);
|
||||
ServiceHost.SetRequestHandler(ExecutionPlanComparisonRequest.Type, HandleExecutionPlanComparisonRequest);
|
||||
}
|
||||
|
||||
private async Task HandleGetExecutionPlan(GetExecutionPlanParams requestParams, RequestContext<GetExecutionPlanResult> requestContext)
|
||||
{
|
||||
try
|
||||
{
|
||||
var plans = ShowPlanGraphUtils.CreateShowPlanGraph(requestParams.GraphInfo.GraphFileContent, "");
|
||||
var plans = ExecutionPlanGraphUtils.CreateShowPlanGraph(requestParams.GraphInfo.GraphFileContent, "");
|
||||
await requestContext.SendResult(new GetExecutionPlanResult
|
||||
{
|
||||
Graphs = plans
|
||||
@@ -70,7 +70,46 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the ShowPlan Service
|
||||
/// Handles requests for color matching similar nodes.
|
||||
/// </summary>
|
||||
internal async Task HandleExecutionPlanComparisonRequest(
|
||||
ExecutionPlanComparisonParams requestParams,
|
||||
RequestContext<ExecutionPlanComparisonResult> requestContext)
|
||||
{
|
||||
try
|
||||
{
|
||||
var firstGraphSet = ShowPlanGraph.ParseShowPlanXML(requestParams.FirstExecutionPlanGraphInfo.GraphFileContent, ShowPlanType.Unknown);
|
||||
var firstRootNode = firstGraphSet?[0]?.Root;
|
||||
|
||||
var secondGraphSet = ShowPlanGraph.ParseShowPlanXML(requestParams.SecondExecutionPlanGraphInfo.GraphFileContent, ShowPlanType.Unknown);
|
||||
var secondRootNode = secondGraphSet?[0]?.Root;
|
||||
|
||||
var manager = new SkeletonManager();
|
||||
var firstSkeletonNode = manager.CreateSkeleton(firstRootNode);
|
||||
var secondSkeletonNode = manager.CreateSkeleton(secondRootNode);
|
||||
manager.ColorMatchingSections(firstSkeletonNode, secondSkeletonNode, requestParams.IgnoreDatabaseName);
|
||||
|
||||
var firstGraphComparisonResultDTO = firstSkeletonNode.ConvertToDTO();
|
||||
var secondGraphComparisonResultDTO = secondSkeletonNode.ConvertToDTO();
|
||||
ExecutionPlanGraphUtils.CopyMatchingNodesIntoSkeletonDTO(firstGraphComparisonResultDTO, secondGraphComparisonResultDTO);
|
||||
ExecutionPlanGraphUtils.CopyMatchingNodesIntoSkeletonDTO(secondGraphComparisonResultDTO, firstGraphComparisonResultDTO);
|
||||
|
||||
var result = new ExecutionPlanComparisonResult()
|
||||
{
|
||||
FirstComparisonResult = firstGraphComparisonResultDTO,
|
||||
SecondComparisonResult = secondGraphComparisonResultDTO
|
||||
};
|
||||
|
||||
await requestContext.SendResult(result);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await requestContext.SendError(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the Execution Plan Service
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan.Comparison
|
||||
/// </summary>
|
||||
/// <param name="root">Node to construct skeleton of</param>
|
||||
/// <returns>SkeletonNode with children representing logical descendants of the input node</returns>
|
||||
public SkeletonNode CreateSkeleton(Node root)
|
||||
public SkeletonNode CreateSkeleton(Node root)
|
||||
{
|
||||
Node rootNode = root;
|
||||
var childCount = root.Children.Count;
|
||||
@@ -36,7 +36,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan.Comparison
|
||||
}
|
||||
return skeletonParent;
|
||||
}
|
||||
else if (childCount == 1)
|
||||
else if (childCount == 1)
|
||||
{
|
||||
if (!ShouldIgnoreDuringComparison(rootNode))
|
||||
{
|
||||
@@ -63,10 +63,14 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan.Comparison
|
||||
public bool AreSkeletonsEquivalent(SkeletonNode root1, SkeletonNode root2, bool ignoreDatabaseName)
|
||||
{
|
||||
if (root1 == null && root2 == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (root1 == null || root2 == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!root1.BaseNode.IsLogicallyEquivalentTo(root2.BaseNode, ignoreDatabaseName))
|
||||
{
|
||||
@@ -80,7 +84,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan.Comparison
|
||||
while (childIterator < root1.Children.Count)
|
||||
{
|
||||
var checkMatch = AreSkeletonsEquivalent(root1.Children.ElementAt(childIterator), root2.Children.ElementAt(childIterator), ignoreDatabaseName);
|
||||
if (!checkMatch)
|
||||
if (!checkMatch)
|
||||
{
|
||||
// at least one pair of children (ie inner.Child1 & outer.Child1) didn't match; stop checking rest
|
||||
return false;
|
||||
|
||||
@@ -4,19 +4,21 @@
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan.Comparison
|
||||
{
|
||||
public class SkeletonNode
|
||||
{
|
||||
public Node BaseNode {get; set;}
|
||||
public Node BaseNode { get; set; }
|
||||
public List<SkeletonNode> MatchingNodes { get; set; }
|
||||
public bool HasMatch { get { return MatchingNodes.Count > 0; } }
|
||||
public SkeletonNode ParentNode { get; set; }
|
||||
public IList<SkeletonNode> Children { get; set; }
|
||||
|
||||
public int GroupIndex
|
||||
{
|
||||
public int GroupIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.BaseNode.GroupIndex;
|
||||
@@ -54,7 +56,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan.Comparison
|
||||
}
|
||||
}
|
||||
|
||||
public void AddMatchingSkeletonNode(SkeletonNode match, bool ignoreDatabaseName, bool matchAllChildren=true)
|
||||
public void AddMatchingSkeletonNode(SkeletonNode match, bool ignoreDatabaseName, bool matchAllChildren = true)
|
||||
{
|
||||
this.BaseNode[NodeBuilderConstants.SkeletonHasMatch] = true;
|
||||
if (matchAllChildren == true)
|
||||
@@ -80,5 +82,43 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan.Comparison
|
||||
return this.BaseNode.Graph;
|
||||
}
|
||||
|
||||
public ExecutionGraphComparisonResult ConvertToDTO()
|
||||
{
|
||||
var queue = new Queue<SkeletonNode>();
|
||||
queue.Enqueue(this);
|
||||
|
||||
var skeletonNodeDTO = new ExecutionGraphComparisonResult();
|
||||
var dtoQueue = new Queue<ExecutionGraphComparisonResult>();
|
||||
dtoQueue.Enqueue(skeletonNodeDTO);
|
||||
|
||||
while (queue.Count != 0)
|
||||
{
|
||||
var curNode = queue.Dequeue();
|
||||
var curNodeDTO = dtoQueue.Dequeue();
|
||||
|
||||
curNodeDTO.BaseNode = ExecutionPlanGraphUtils.ConvertShowPlanTreeToExecutionPlanTree(curNode.BaseNode);
|
||||
curNodeDTO.GroupIndex = curNode.GroupIndex;
|
||||
curNodeDTO.HasMatch = curNode.HasMatch;
|
||||
curNodeDTO.MatchingNodes = curNode.MatchingNodes.Select(matchingNode =>
|
||||
{
|
||||
var skeletonNodeDTO = new Contracts.ExecutionGraphComparisonResult();
|
||||
skeletonNodeDTO.BaseNode = ExecutionPlanGraphUtils.ConvertShowPlanTreeToExecutionPlanTree(matchingNode.BaseNode);
|
||||
|
||||
return skeletonNodeDTO;
|
||||
}).ToList();
|
||||
|
||||
foreach (var child in curNode.Children)
|
||||
{
|
||||
queue.Enqueue(child);
|
||||
|
||||
var childDTO = new Contracts.ExecutionGraphComparisonResult();
|
||||
childDTO.ParentNode = curNodeDTO;
|
||||
curNodeDTO.Children.Add(childDTO);
|
||||
dtoQueue.Enqueue(childDTO);
|
||||
}
|
||||
}
|
||||
|
||||
return skeletonNodeDTO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan
|
||||
{
|
||||
public class Graph
|
||||
{
|
||||
public Node Root;
|
||||
public Node Root { get; set; }
|
||||
|
||||
public Description Description;
|
||||
public Description Description { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ using Microsoft.SqlTools.ServiceLayer.Workspace;
|
||||
using Microsoft.SqlTools.ServiceLayer.NotebookConvert;
|
||||
using Microsoft.SqlTools.ServiceLayer.ModelManagement;
|
||||
using Microsoft.SqlTools.ServiceLayer.TableDesigner;
|
||||
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer
|
||||
{
|
||||
@@ -166,8 +167,8 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
InitializeHostedServices(serviceProvider, serviceHost);
|
||||
serviceHost.ServiceProvider = serviceProvider;
|
||||
|
||||
ExecutionPlan.ExecutionPlanService.Instance.InitializeService(serviceHost);
|
||||
serviceProvider.RegisterSingleService(ExecutionPlan.ExecutionPlanService.Instance);
|
||||
ExecutionPlanService.Instance.InitializeService(serviceHost);
|
||||
serviceProvider.RegisterSingleService(ExecutionPlanService.Instance);
|
||||
|
||||
serviceHost.InitializeRequestHandlers();
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan;
|
||||
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts.ExecuteRequests
|
||||
{
|
||||
|
||||
@@ -21,6 +21,7 @@ using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Workspace;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
{
|
||||
@@ -908,7 +909,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
var xmlString = r.GetRow(0)[0].DisplayValue;
|
||||
try
|
||||
{
|
||||
plans = ShowPlanGraphUtils.CreateShowPlanGraph(xmlString, Path.GetFileName(ownerUri));
|
||||
plans = ExecutionPlanGraphUtils.CreateShowPlanGraph(xmlString, Path.GetFileName(ownerUri));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -9,7 +9,9 @@ using System.IO;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework;
|
||||
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan;
|
||||
|
||||
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan;
|
||||
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan.Comparison;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ShowPlan
|
||||
{
|
||||
@@ -21,18 +23,265 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ShowPlan
|
||||
public void ParseXMLFileReturnsValidShowPlanGraph()
|
||||
{
|
||||
ReadFile(".ShowPlan.TestShowPlan.xml");
|
||||
var showPlanGraphs = ShowPlanGraphUtils.CreateShowPlanGraph(queryPlanFileText, "testFile.sql");
|
||||
var showPlanGraphs = ExecutionPlanGraphUtils.CreateShowPlanGraph(queryPlanFileText, "testFile.sql");
|
||||
Assert.AreEqual(1, showPlanGraphs.Count, "exactly one show plan graph should be returned");
|
||||
Assert.NotNull(showPlanGraphs[0], "graph should not be null");
|
||||
Assert.NotNull(showPlanGraphs[0].Root, "graph should have a root");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CompareShowPlan_CreateSkeleton()
|
||||
{
|
||||
ReadFile(".ShowPlan.TestShowPlan.xml");
|
||||
var graphs = ShowPlanGraph.ParseShowPlanXML(this.queryPlanFileText, ShowPlanType.Unknown);
|
||||
var rootNode = graphs[0].Root;
|
||||
|
||||
Assert.NotNull(rootNode, "graph should have a root");
|
||||
|
||||
var skeletonManager = new SkeletonManager();
|
||||
var skeletonNode = skeletonManager.CreateSkeleton(rootNode);
|
||||
|
||||
Assert.NotNull(skeletonNode, "skeletonNode should not be null");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CompareShowPlan_CreateSkeletonUsingDefaultConfiguredNode()
|
||||
{
|
||||
var graph = new ShowPlanGraph()
|
||||
{
|
||||
Root = null,
|
||||
Description = new Description()
|
||||
};
|
||||
var context = new NodeBuilderContext(graph, ShowPlanType.Unknown, null);
|
||||
var node = new Node(default, context);
|
||||
|
||||
var skeletonManager = new SkeletonManager();
|
||||
var skeletonNode = skeletonManager.CreateSkeleton(node);
|
||||
|
||||
Assert.NotNull(skeletonNode, "skeletonNode should not be null");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CompareShowPlan_DuplicateSkeletons()
|
||||
{
|
||||
ReadFile(".ShowPlan.TestShowPlan.xml");
|
||||
var graphs = ShowPlanGraph.ParseShowPlanXML(this.queryPlanFileText, ShowPlanType.Unknown);
|
||||
var rootNode = graphs[0].Root;
|
||||
|
||||
var skeletonManager = new SkeletonManager();
|
||||
var skeletonNode = skeletonManager.CreateSkeleton(rootNode);
|
||||
var skeletonNode2 = skeletonManager.CreateSkeleton(rootNode);
|
||||
|
||||
var skeletonCompareResult = skeletonManager.AreSkeletonsEquivalent(skeletonNode, skeletonNode2, ignoreDatabaseName: true);
|
||||
|
||||
Assert.IsTrue(skeletonCompareResult);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CompareShowPlan_TwoDefaultConfiguredSkeletons()
|
||||
{
|
||||
var graph = new ShowPlanGraph()
|
||||
{
|
||||
Root = null,
|
||||
Description = new Description()
|
||||
};
|
||||
var context = new NodeBuilderContext(graph, ShowPlanType.Unknown, null);
|
||||
var firstRoot = new Node(default, context);
|
||||
var secondRoot = new Node(default, context);
|
||||
|
||||
var skeletonManager = new SkeletonManager();
|
||||
var firstSkeletonNode = skeletonManager.CreateSkeleton(firstRoot);
|
||||
var secondSkeletonNode = skeletonManager.CreateSkeleton(secondRoot);
|
||||
|
||||
var skeletonCompareResult = skeletonManager.AreSkeletonsEquivalent(firstSkeletonNode, secondSkeletonNode, ignoreDatabaseName: true);
|
||||
|
||||
Assert.IsTrue(skeletonCompareResult, "The two compared skeleton nodes should be equivalent");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CompareShowPlan_ComparingSkeletonAgainstNull()
|
||||
{
|
||||
ReadFile(".ShowPlan.TestShowPlan.xml");
|
||||
var graphs = ShowPlanGraph.ParseShowPlanXML(this.queryPlanFileText, ShowPlanType.Unknown);
|
||||
var rootNode = graphs[0].Root;
|
||||
|
||||
var skeletonManager = new SkeletonManager();
|
||||
var skeletonNode = skeletonManager.CreateSkeleton(rootNode);
|
||||
SkeletonNode skeletonNode2 = null;
|
||||
|
||||
var skeletonCompareResult = skeletonManager.AreSkeletonsEquivalent(skeletonNode, skeletonNode2, ignoreDatabaseName: true);
|
||||
|
||||
Assert.IsFalse(skeletonCompareResult);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CompareShowPlan_DifferentSkeletonChildCount()
|
||||
{
|
||||
ReadFile(".ShowPlan.TestShowPlan.xml");
|
||||
var graphs = ShowPlanGraph.ParseShowPlanXML(this.queryPlanFileText, ShowPlanType.Unknown);
|
||||
var rootNode = graphs[0].Root;
|
||||
|
||||
var skeletonManager = new SkeletonManager();
|
||||
var skeletonNode = skeletonManager.CreateSkeleton(rootNode);
|
||||
var skeletonNode2 = skeletonManager.CreateSkeleton(rootNode);
|
||||
skeletonNode2.Children.RemoveAt(skeletonNode2.Children.Count - 1);
|
||||
|
||||
var skeletonCompareResult = skeletonManager.AreSkeletonsEquivalent(skeletonNode, skeletonNode2, ignoreDatabaseName: true);
|
||||
|
||||
Assert.IsFalse(skeletonCompareResult);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CompareShowPlan_ColorMatchingSectionsWithEquivalentSkeletons()
|
||||
{
|
||||
ReadFile(".ShowPlan.TestShowPlan.xml");
|
||||
var graphs = ShowPlanGraph.ParseShowPlanXML(this.queryPlanFileText, ShowPlanType.Unknown);
|
||||
var rootNode = graphs[0].Root;
|
||||
|
||||
var skeletonManager = new SkeletonManager();
|
||||
var skeletonNode = skeletonManager.CreateSkeleton(rootNode);
|
||||
var skeletonNode2 = skeletonManager.CreateSkeleton(rootNode);
|
||||
|
||||
Assert.IsFalse(skeletonNode.HasMatch);
|
||||
Assert.IsFalse(skeletonNode2.HasMatch);
|
||||
|
||||
Assert.Zero(skeletonNode.MatchingNodes.Count);
|
||||
Assert.Zero(skeletonNode2.MatchingNodes.Count);
|
||||
|
||||
skeletonManager.ColorMatchingSections(skeletonNode, skeletonNode2, ignoreDatabaseName: true);
|
||||
|
||||
Assert.IsTrue(skeletonNode.HasMatch);
|
||||
Assert.IsTrue(skeletonNode2.HasMatch);
|
||||
|
||||
Assert.AreEqual(1, skeletonNode.MatchingNodes.Count);
|
||||
Assert.AreEqual(1, skeletonNode2.MatchingNodes.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CompareShowPlan_ColorMatchingSectionsWithNullAndNonNullSkeleton()
|
||||
{
|
||||
ReadFile(".ShowPlan.TestShowPlan.xml");
|
||||
var graphs = ShowPlanGraph.ParseShowPlanXML(this.queryPlanFileText, ShowPlanType.Unknown);
|
||||
var rootNode = graphs[0].Root;
|
||||
|
||||
var skeletonManager = new SkeletonManager();
|
||||
SkeletonNode nullSkeletonNode = null;
|
||||
var skeletonNode2 = skeletonManager.CreateSkeleton(rootNode);
|
||||
|
||||
skeletonManager.ColorMatchingSections(nullSkeletonNode, skeletonNode2, ignoreDatabaseName: true);
|
||||
|
||||
Assert.IsNull(nullSkeletonNode);
|
||||
Assert.IsFalse(skeletonNode2.HasMatch);
|
||||
Assert.Zero(skeletonNode2.MatchingNodes.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CompareShowPlan_ColorMatchingSectionsWithTwoNullSKeletons()
|
||||
{
|
||||
var skeletonManager = new SkeletonManager();
|
||||
SkeletonNode skeletonNode = null;
|
||||
SkeletonNode skeletonNode2 = null;
|
||||
|
||||
skeletonManager.ColorMatchingSections(skeletonNode, skeletonNode2, ignoreDatabaseName: true);
|
||||
|
||||
Assert.IsNull(skeletonNode);
|
||||
Assert.IsNull(skeletonNode2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CompareShowPlan_ColorMatchingSectionsWithNonNullAndNullSkeleton()
|
||||
{
|
||||
ReadFile(".ShowPlan.TestShowPlan.xml");
|
||||
var graphs = ShowPlanGraph.ParseShowPlanXML(this.queryPlanFileText, ShowPlanType.Unknown);
|
||||
var rootNode = graphs[0].Root;
|
||||
|
||||
var skeletonManager = new SkeletonManager();
|
||||
var skeletonNode = skeletonManager.CreateSkeleton(rootNode);
|
||||
SkeletonNode nullSkeletonNode = null;
|
||||
|
||||
skeletonManager.ColorMatchingSections(skeletonNode, nullSkeletonNode, ignoreDatabaseName: true);
|
||||
|
||||
Assert.IsFalse(skeletonNode.HasMatch);
|
||||
Assert.Zero(skeletonNode.MatchingNodes.Count);
|
||||
Assert.IsNull(nullSkeletonNode);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CompareShowPlan_ColorMatchingSectionsWithDifferentChildCount()
|
||||
{
|
||||
ReadFile(".ShowPlan.TestShowPlan.xml");
|
||||
var graphs = ShowPlanGraph.ParseShowPlanXML(this.queryPlanFileText, ShowPlanType.Unknown);
|
||||
var rootNode = graphs[0].Root;
|
||||
|
||||
var skeletonManager = new SkeletonManager();
|
||||
var skeletonNode = skeletonManager.CreateSkeleton(rootNode);
|
||||
var skeletonNode2 = skeletonManager.CreateSkeleton(rootNode);
|
||||
skeletonNode2.Children.RemoveAt(skeletonNode2.Children.Count - 1);
|
||||
|
||||
Assert.IsFalse(skeletonNode.Children[0].HasMatch);
|
||||
Assert.IsFalse(skeletonNode2.Children[0].HasMatch);
|
||||
|
||||
Assert.Zero(skeletonNode.Children[0].MatchingNodes.Count);
|
||||
Assert.Zero(skeletonNode2.Children[0].MatchingNodes.Count);
|
||||
|
||||
skeletonManager.ColorMatchingSections(skeletonNode, skeletonNode2, ignoreDatabaseName: true);
|
||||
|
||||
Assert.IsTrue(skeletonNode.Children[0].HasMatch);
|
||||
Assert.IsTrue(skeletonNode2.Children[0].HasMatch);
|
||||
|
||||
Assert.AreEqual(1, skeletonNode.Children[0].MatchingNodes.Count);
|
||||
Assert.AreEqual(1, skeletonNode2.Children[0].MatchingNodes.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CompareShowPlan_FindNextNonIgnoreNode()
|
||||
{
|
||||
ReadFile(".ShowPlan.TestShowPlan.xml");
|
||||
var graphs = ShowPlanGraph.ParseShowPlanXML(this.queryPlanFileText, ShowPlanType.Unknown);
|
||||
var rootNode = graphs[0].Root;
|
||||
|
||||
var skeletonManager = new SkeletonManager();
|
||||
var result = skeletonManager.FindNextNonIgnoreNode(rootNode);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.AreEqual("InnerJoin", result.LogicalOpUnlocName);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CompareShowPlan_FindNextNonIgnoreNodeWithChildlessRoot()
|
||||
{
|
||||
ReadFile(".ShowPlan.TestShowPlan.xml");
|
||||
var graphs = ShowPlanGraph.ParseShowPlanXML(this.queryPlanFileText, ShowPlanType.Unknown);
|
||||
var rootNode = graphs[0].Root;
|
||||
|
||||
for (int childIndex = 0; childIndex < rootNode.Children.Count; ++childIndex)
|
||||
{
|
||||
rootNode.Children.RemoveAt(0);
|
||||
}
|
||||
|
||||
var skeletonManager = new SkeletonManager();
|
||||
var result = skeletonManager.FindNextNonIgnoreNode(rootNode);
|
||||
|
||||
Assert.IsNull(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CompareShowPlan_FindNextNonIgnoreNodeWithNullNode()
|
||||
{
|
||||
Node rootNode = null;
|
||||
|
||||
var skeletonManager = new SkeletonManager();
|
||||
var result = skeletonManager.FindNextNonIgnoreNode(rootNode);
|
||||
|
||||
Assert.IsNull(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParsingNestedProperties()
|
||||
{
|
||||
ReadFile(".ShowPlan.TestShowPlan.xml");
|
||||
string[] commonNestedPropertiesNames = { "MemoryGrantInfo", "OptimizerHardwareDependentProperties" };
|
||||
var showPlanGraphs = ShowPlanGraphUtils.CreateShowPlanGraph(queryPlanFileText, "testFile.sql");
|
||||
var showPlanGraphs = ExecutionPlanGraphUtils.CreateShowPlanGraph(queryPlanFileText, "testFile.sql");
|
||||
ExecutionPlanNode rootNode = showPlanGraphs[0].Root;
|
||||
rootNode.Properties.ForEach(p =>
|
||||
{
|
||||
@@ -49,7 +298,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ShowPlan
|
||||
//The first graph in this execution plan has 3 recommendations
|
||||
ReadFile(".ShowPlan.TestShowPlanRecommendations.xml");
|
||||
string[] commonNestedPropertiesNames = { "MemoryGrantInfo", "OptimizerHardwareDependentProperties" };
|
||||
var showPlanGraphs = ShowPlanGraphUtils.CreateShowPlanGraph(queryPlanFileText, "testFile.sql");
|
||||
var showPlanGraphs = ExecutionPlanGraphUtils.CreateShowPlanGraph(queryPlanFileText, "testFile.sql");
|
||||
List<ExecutionPlanRecommendation> rootNode = showPlanGraphs[0].Recommendations;
|
||||
Assert.AreEqual(3, rootNode.Count, "3 recommendations should be returned by the showplan parser");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user