mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 10:58:30 -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;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan.Contracts
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Execution plan graph object that is sent over JSON RPC
|
/// Execution plan graph object that is sent over JSON RPC
|
||||||
@@ -32,6 +32,10 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
|||||||
|
|
||||||
public class ExecutionPlanNode
|
public class ExecutionPlanNode
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ID for the node.
|
||||||
|
/// </summary>
|
||||||
|
public int ID { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Type of the node. This determines the icon that is displayed for it
|
/// Type of the node. This determines the icon that is displayed for it
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,14 +10,15 @@ using System.Linq;
|
|||||||
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan;
|
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan;
|
||||||
using Microsoft.SqlTools.Utility;
|
using Microsoft.SqlTools.Utility;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
||||||
{
|
{
|
||||||
public class ShowPlanGraphUtils
|
public class ExecutionPlanGraphUtils
|
||||||
{
|
{
|
||||||
public static List<ExecutionPlanGraph> CreateShowPlanGraph(string xml, string fileName)
|
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
|
return graphs.Select(g => new ExecutionPlanGraph
|
||||||
{
|
{
|
||||||
Root = ConvertShowPlanTreeToExecutionPlanTree(g.Root),
|
Root = ConvertShowPlanTreeToExecutionPlanTree(g.Root),
|
||||||
@@ -31,10 +32,11 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
|||||||
}).ToList();
|
}).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ExecutionPlanNode ConvertShowPlanTreeToExecutionPlanTree(Node currentNode)
|
public static ExecutionPlanNode ConvertShowPlanTreeToExecutionPlanTree(Node currentNode)
|
||||||
{
|
{
|
||||||
return new ExecutionPlanNode
|
return new ExecutionPlanNode
|
||||||
{
|
{
|
||||||
|
ID = currentNode.ID,
|
||||||
Type = currentNode.Operation.Image,
|
Type = currentNode.Operation.Image,
|
||||||
Cost = currentNode.Cost,
|
Cost = currentNode.Cost,
|
||||||
SubTreeCost = currentNode.SubtreeCost,
|
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
|
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>();
|
List<ExecutionPlanGraphPropertyBase> propsList = new List<ExecutionPlanGraphPropertyBase>();
|
||||||
foreach (PropertyValue prop in props)
|
foreach (PropertyValue prop in props)
|
||||||
@@ -96,7 +98,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
|||||||
return propsList;
|
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
|
return g.Description.MissingIndices.Select(mi => new ExecutionPlanRecommendation
|
||||||
{
|
{
|
||||||
@@ -148,5 +150,58 @@ GO
|
|||||||
return String.Empty;
|
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;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.SqlTools.Hosting.Protocol;
|
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;
|
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Main class for Migration Service functionality
|
/// Main class for Execution Plan Service functionality
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class ExecutionPlanService : IDisposable
|
public sealed class ExecutionPlanService : IDisposable
|
||||||
{
|
{
|
||||||
@@ -20,9 +23,9 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
|||||||
private bool disposed;
|
private bool disposed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Construct a new MigrationService instance with default parameters
|
/// Construct a new Execution Plan Service instance with default parameters
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ExecutionPlanService()
|
private ExecutionPlanService()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,26 +41,23 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
|||||||
/// Service host object for sending/receiving requests/events.
|
/// Service host object for sending/receiving requests/events.
|
||||||
/// Internal for testing purposes.
|
/// Internal for testing purposes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal IProtocolEndpoint ServiceHost
|
internal IProtocolEndpoint ServiceHost { get; set; }
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes the ShowPlan Service instance
|
/// Initializes the Execution Plan Service instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void InitializeService(ServiceHost serviceHost)
|
public void InitializeService(ServiceHost serviceHost)
|
||||||
{
|
{
|
||||||
this.ServiceHost = serviceHost;
|
ServiceHost = serviceHost;
|
||||||
this.ServiceHost.SetRequestHandler(GetExecutionPlanRequest.Type, HandleGetExecutionPlan);
|
ServiceHost.SetRequestHandler(GetExecutionPlanRequest.Type, HandleGetExecutionPlan);
|
||||||
|
ServiceHost.SetRequestHandler(ExecutionPlanComparisonRequest.Type, HandleExecutionPlanComparisonRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleGetExecutionPlan(GetExecutionPlanParams requestParams, RequestContext<GetExecutionPlanResult> requestContext)
|
private async Task HandleGetExecutionPlan(GetExecutionPlanParams requestParams, RequestContext<GetExecutionPlanResult> requestContext)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var plans = ShowPlanGraphUtils.CreateShowPlanGraph(requestParams.GraphInfo.GraphFileContent, "");
|
var plans = ExecutionPlanGraphUtils.CreateShowPlanGraph(requestParams.GraphInfo.GraphFileContent, "");
|
||||||
await requestContext.SendResult(new GetExecutionPlanResult
|
await requestContext.SendResult(new GetExecutionPlanResult
|
||||||
{
|
{
|
||||||
Graphs = plans
|
Graphs = plans
|
||||||
@@ -70,7 +70,46 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan.Comparison
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="root">Node to construct skeleton of</param>
|
/// <param name="root">Node to construct skeleton of</param>
|
||||||
/// <returns>SkeletonNode with children representing logical descendants of the input node</returns>
|
/// <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;
|
Node rootNode = root;
|
||||||
var childCount = root.Children.Count;
|
var childCount = root.Children.Count;
|
||||||
@@ -63,10 +63,14 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan.Comparison
|
|||||||
public bool AreSkeletonsEquivalent(SkeletonNode root1, SkeletonNode root2, bool ignoreDatabaseName)
|
public bool AreSkeletonsEquivalent(SkeletonNode root1, SkeletonNode root2, bool ignoreDatabaseName)
|
||||||
{
|
{
|
||||||
if (root1 == null && root2 == null)
|
if (root1 == null && root2 == null)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (root1 == null || root2 == null)
|
if (root1 == null || root2 == null)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!root1.BaseNode.IsLogicallyEquivalentTo(root2.BaseNode, ignoreDatabaseName))
|
if (!root1.BaseNode.IsLogicallyEquivalentTo(root2.BaseNode, ignoreDatabaseName))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,12 +4,14 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan.Comparison
|
namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan.Comparison
|
||||||
{
|
{
|
||||||
public class SkeletonNode
|
public class SkeletonNode
|
||||||
{
|
{
|
||||||
public Node BaseNode {get; set;}
|
public Node BaseNode { get; set; }
|
||||||
public List<SkeletonNode> MatchingNodes { get; set; }
|
public List<SkeletonNode> MatchingNodes { get; set; }
|
||||||
public bool HasMatch { get { return MatchingNodes.Count > 0; } }
|
public bool HasMatch { get { return MatchingNodes.Count > 0; } }
|
||||||
public SkeletonNode ParentNode { get; set; }
|
public SkeletonNode ParentNode { get; set; }
|
||||||
@@ -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;
|
this.BaseNode[NodeBuilderConstants.SkeletonHasMatch] = true;
|
||||||
if (matchAllChildren == true)
|
if (matchAllChildren == true)
|
||||||
@@ -80,5 +82,43 @@ namespace Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan.Comparison
|
|||||||
return this.BaseNode.Graph;
|
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 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.NotebookConvert;
|
||||||
using Microsoft.SqlTools.ServiceLayer.ModelManagement;
|
using Microsoft.SqlTools.ServiceLayer.ModelManagement;
|
||||||
using Microsoft.SqlTools.ServiceLayer.TableDesigner;
|
using Microsoft.SqlTools.ServiceLayer.TableDesigner;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer
|
namespace Microsoft.SqlTools.ServiceLayer
|
||||||
{
|
{
|
||||||
@@ -166,8 +167,8 @@ namespace Microsoft.SqlTools.ServiceLayer
|
|||||||
InitializeHostedServices(serviceProvider, serviceHost);
|
InitializeHostedServices(serviceProvider, serviceHost);
|
||||||
serviceHost.ServiceProvider = serviceProvider;
|
serviceHost.ServiceProvider = serviceProvider;
|
||||||
|
|
||||||
ExecutionPlan.ExecutionPlanService.Instance.InitializeService(serviceHost);
|
ExecutionPlanService.Instance.InitializeService(serviceHost);
|
||||||
serviceProvider.RegisterSingleService(ExecutionPlan.ExecutionPlanService.Instance);
|
serviceProvider.RegisterSingleService(ExecutionPlanService.Instance);
|
||||||
|
|
||||||
serviceHost.InitializeRequestHandlers();
|
serviceHost.InitializeRequestHandlers();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan;
|
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts.ExecuteRequests
|
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.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Workspace;
|
using Microsoft.SqlTools.ServiceLayer.Workspace;
|
||||||
using Microsoft.SqlTools.Utility;
|
using Microsoft.SqlTools.Utility;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||||
{
|
{
|
||||||
@@ -908,7 +909,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
var xmlString = r.GetRow(0)[0].DisplayValue;
|
var xmlString = r.GetRow(0)[0].DisplayValue;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
plans = ShowPlanGraphUtils.CreateShowPlanGraph(xmlString, Path.GetFileName(ownerUri));
|
plans = ExecutionPlanGraphUtils.CreateShowPlanGraph(xmlString, Path.GetFileName(ownerUri));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ using System.IO;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan;
|
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
|
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ShowPlan
|
||||||
{
|
{
|
||||||
@@ -21,18 +23,265 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ShowPlan
|
|||||||
public void ParseXMLFileReturnsValidShowPlanGraph()
|
public void ParseXMLFileReturnsValidShowPlanGraph()
|
||||||
{
|
{
|
||||||
ReadFile(".ShowPlan.TestShowPlan.xml");
|
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.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], "graph should not be null");
|
||||||
Assert.NotNull(showPlanGraphs[0].Root, "graph should have a root");
|
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]
|
[Test]
|
||||||
public void ParsingNestedProperties()
|
public void ParsingNestedProperties()
|
||||||
{
|
{
|
||||||
ReadFile(".ShowPlan.TestShowPlan.xml");
|
ReadFile(".ShowPlan.TestShowPlan.xml");
|
||||||
string[] commonNestedPropertiesNames = { "MemoryGrantInfo", "OptimizerHardwareDependentProperties" };
|
string[] commonNestedPropertiesNames = { "MemoryGrantInfo", "OptimizerHardwareDependentProperties" };
|
||||||
var showPlanGraphs = ShowPlanGraphUtils.CreateShowPlanGraph(queryPlanFileText, "testFile.sql");
|
var showPlanGraphs = ExecutionPlanGraphUtils.CreateShowPlanGraph(queryPlanFileText, "testFile.sql");
|
||||||
ExecutionPlanNode rootNode = showPlanGraphs[0].Root;
|
ExecutionPlanNode rootNode = showPlanGraphs[0].Root;
|
||||||
rootNode.Properties.ForEach(p =>
|
rootNode.Properties.ForEach(p =>
|
||||||
{
|
{
|
||||||
@@ -49,7 +298,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ShowPlan
|
|||||||
//The first graph in this execution plan has 3 recommendations
|
//The first graph in this execution plan has 3 recommendations
|
||||||
ReadFile(".ShowPlan.TestShowPlanRecommendations.xml");
|
ReadFile(".ShowPlan.TestShowPlanRecommendations.xml");
|
||||||
string[] commonNestedPropertiesNames = { "MemoryGrantInfo", "OptimizerHardwareDependentProperties" };
|
string[] commonNestedPropertiesNames = { "MemoryGrantInfo", "OptimizerHardwareDependentProperties" };
|
||||||
var showPlanGraphs = ShowPlanGraphUtils.CreateShowPlanGraph(queryPlanFileText, "testFile.sql");
|
var showPlanGraphs = ExecutionPlanGraphUtils.CreateShowPlanGraph(queryPlanFileText, "testFile.sql");
|
||||||
List<ExecutionPlanRecommendation> rootNode = showPlanGraphs[0].Recommendations;
|
List<ExecutionPlanRecommendation> rootNode = showPlanGraphs[0].Recommendations;
|
||||||
Assert.AreEqual(3, rootNode.Count, "3 recommendations should be returned by the showplan parser");
|
Assert.AreEqual(3, rootNode.Count, "3 recommendations should be returned by the showplan parser");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user